/*
* 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.javaDSL;
import net.sf.sahi.client.Browser;
import net.sf.sahi.test.ProcessHelper;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.sakuli.actions.TestCaseAction;
import org.sakuli.datamodel.TestSuite;
import org.sakuli.datamodel.builder.TestCaseBuilder;
import org.sakuli.exceptions.SakuliException;
import org.sakuli.exceptions.SakuliRuntimeException;
import org.sakuli.javaDSL.service.SahiInitializingService;
import org.sakuli.javaDSL.utils.SakuliJavaPropertyPlaceholderConfigurer;
import org.sakuli.loader.BeanLoader;
import org.sakuli.services.InitializingServiceHelper;
import org.sakuli.services.TeardownServiceHelper;
import org.sakuli.utils.ResourceHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import org.testng.annotations.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author Tobias Schneck
*/
@Listeners(value = SakuliExceptionListener.class)
public abstract class AbstractSakuliTest {
public static final String SAKULI_TEST = "sakuli-test";
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSakuliTest.class);
protected ExecutorService executorService;
protected TestCaseAction testCaseAction;
protected Browser browser;
private int counter;
private DateTime startTime;
private DateTime startTimeCase;
private TestCaseInitParameter initParameter;
private TestSuite testSuite;
private String browserProcessName;
public static Path resolveResource(Class<?> aClass, String resourceName) {
try {
return ResourceHelper.getClasspathResource(aClass, resourceName, "cannot resolve resource '" + resourceName + "' from classpath!");
} catch (NoSuchFileException e) {
throw new SakuliRuntimeException("cannot resolve resource '" + resourceName + "' from classpath!", e);
}
}
private static Object getField(Object target, String name) {
if (target != null) {
Field field = ReflectionUtils.findField(target.getClass(), name);
if (field != null) {
ReflectionUtils.makeAccessible(field);
return ReflectionUtils.getField(field, target);
}
}
return null;
}
abstract protected TestCaseInitParameter getTestCaseInitParameter() throws Exception;
/**
* Initialize the Spring context of the Sakuli test suite and invokes all configured Initializing Services.
*
* @throws FileNotFoundException
*/
protected void initTestSuiteParameter() throws FileNotFoundException {
LOGGER.info("............................INITIALIZE SAKULI-CONTEXT");
executorService = Executors.newCachedThreadPool();
BeanLoader.CONTEXT_PATH = "java-dsl-beanRefFactory.xml";
SakuliJavaPropertyPlaceholderConfigurer.TEST_SUITE_FOLDER_VALUE = getTestSuiteFolder();
SakuliJavaPropertyPlaceholderConfigurer.SAKULI_HOME_FOLDER_VALUE = getSakuliHomeFolder();
SakuliJavaPropertyPlaceholderConfigurer.SAHI_HOME_VALUE = getSahiFolder();
InitializingServiceHelper.invokeInitializingServcies();
}
/**
* Override this method to specify a custom Sahi installation folder!
*
* @return the installation folder of Sahi
*/
protected String getSahiFolder() {
String packageName = "/sahi";
Path sahHomeFolder = resolveResource(AbstractSakuliTest.class, packageName);
if (Files.exists(sahHomeFolder)) {
return sahHomeFolder.normalize().toAbsolutePath().toString();
}
throw new SakuliRuntimeException("Cannot load SAHI_HOME folder! Should be normally under 'target/classes/" + packageName + "'");
}
@BeforeClass(alwaysRun = true)
public void initTC() throws Exception {
if (testSuite == null) {
initTestSuiteParameter();
}
LOGGER.info("............................INITIALIZE TEST-CASE with {}", initParameter);
String testCaseName = this.getClass().getSimpleName();
initParameter = getTestCaseInitParameter();
if (initParameter == null) {
throw new SakuliException("init parameter have to be set!");
}
testSuite = BeanLoader.loadBean(TestSuite.class);
//start sahi controlled browser if needed
initSahiBrowser();
testSuite.addTestCase(new TestCaseBuilder(testCaseName, initParameter.getTestCaseId()).build());
testCaseAction = BeanLoader.loadTestCaseAction();
//add the the testcase folder as image folder
if (Files.exists(getTestCaseFolder())) {
initParameter.addImagePath(getTestCaseFolder().toString());
}
initTestCaseAction(initParameter);
LOGGER.info("............................START TEST-CASE '{}' - {}", initParameter.getTestCaseId(), testCaseName);
counter = 0;
startTimeCase = DateTime.now();
}
private void initSahiBrowser() {
browser = BeanLoader.loadBean(SahiInitializingService.class).getBrowser();
browserProcessName = String.valueOf(getField(browser, "browserProcessName"));
}
/**
* Initialize the image folders, warning time and critical time for the current testcase with the assigned
* initParameter.
*
* @param initParameter a initialized object of {@link TestCaseInitParameter}.
*/
protected void initTestCaseAction(TestCaseInitParameter initParameter) {
List<Path> imagePaths = initParameter.getImagePaths();
testCaseAction.initWithPaths(this.initParameter.getTestCaseId(),
this.initParameter.getWarningTime(),
this.initParameter.getCriticalTime(),
imagePaths.toArray(new Path[imagePaths.size()])
);
}
@BeforeMethod(alwaysRun = true)
public void initTcStep() throws Exception {
counter++;
startTime = DateTime.now();
}
@AfterMethod(alwaysRun = true)
public void saveTcStep() throws Throwable {
testCaseAction.addTestCaseStep("step " + counter,
String.valueOf(startTime.getMillis()),
String.valueOf(DateTime.now().getMillis()),
0
);
}
@AfterClass(alwaysRun = true)
public void stopTC() throws Exception {
if (executorService != null) {
executorService.awaitTermination(1, TimeUnit.MILLISECONDS);
}
String testCaseName = this.getClass().getSimpleName();
LOGGER.info("............................ SAVE RESULTS OF TEST-CASE '{}' - {}", initParameter.getTestCaseId(), testCaseName);
testCaseAction.saveResult(initParameter.getTestCaseId(),
String.valueOf(startTimeCase.getMillis()),
String.valueOf(DateTime.now().getMillis()),
null,
null
);
if (browser != null) {
browser.close();
}
}
protected String getTestSuiteRootFolder() {
Path resourcePath = resolveResource(this.getClass(), "/");
if (Files.exists(resourcePath)) {
return resourcePath.normalize().toAbsolutePath().toString();
}
throw new SakuliRuntimeException("Cannot load test suites root folder! Should be at normal test 'src/test/resources'");
}
/**
* @return the matching resource folder of the package of the current class!
*/
protected String getTestSuiteFolder() {
Path suiteFolder = resolveResource(this.getClass(), ".");
if (Files.exists(suiteFolder)) {
return suiteFolder.normalize().toAbsolutePath().toString();
}
throw new SakuliRuntimeException(String.format("Cannot load test suite folder from classpath! Should be at normal test 'src/test/resources/%s'",
StringUtils.replace(this.getClass().getCanonicalName(), ".", "/")));
}
protected Path getTestCaseFolder() {
return Paths.get(getTestSuiteFolder() + File.separator + initParameter.getTestCaseFolderName());
}
protected String getSakuliHomeFolder() {
String packageName = "/org/sakuli/common";
Path sakuliHomeFolder = resolveResource(AbstractSakuliTest.class, packageName);
if (Files.exists(sakuliHomeFolder)) {
return sakuliHomeFolder.normalize().toAbsolutePath().toString();
}
throw new SakuliRuntimeException("Cannot load SAKULI_HOME folder! Should be normally under 'target/classes/" + packageName + "'");
}
@AfterSuite(alwaysRun = true)
public void tearDown() throws Exception {
if (testSuite != null) {
LOGGER.info("========== TEAR-DOWN SAKULI TEST SUITE '{}' ==========", testSuite.getId());
testSuite.setStopDate(DateTime.now().toDate());
TeardownServiceHelper.invokeTeardownServices();
}
if (executorService != null) {
executorService.shutdownNow();
}
if (StringUtils.isNotEmpty(browserProcessName) && !browserProcessName.equals("null")) {
LOGGER.info("kill browser process '{}'", browserProcessName);
ProcessHelper.killAll(browserProcessName);
}
}
}