org.sahagin.runlib.runresultsgen.RunResultGenerateHook Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sahagin Show documentation
Show all versions of sahagin Show documentation
Sahagin makes your Selenium script more readable and maintainable.
package org.sahagin.runlib.runresultsgen;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.openqa.selenium.io.IOUtils;
import org.sahagin.report.HtmlReport;
import org.sahagin.runlib.external.CaptureStyle;
import org.sahagin.runlib.external.adapter.AdapterContainer;
import org.sahagin.share.CommonPath;
import org.sahagin.share.Config;
import org.sahagin.share.IllegalDataStructureException;
import org.sahagin.share.IllegalTestScriptException;
import org.sahagin.share.Logging;
import org.sahagin.share.runresults.LineScreenCapture;
import org.sahagin.share.runresults.RootMethodRunResult;
import org.sahagin.share.runresults.RunFailure;
import org.sahagin.share.runresults.StackLine;
import org.sahagin.share.srctree.SrcTree;
import org.sahagin.share.srctree.TestMethod;
import org.sahagin.share.srctree.code.CodeLine;
import org.sahagin.share.srctree.code.UnknownCode;
import org.sahagin.share.yaml.YamlConvertException;
import org.sahagin.share.yaml.YamlUtils;
// TODO support multiple thread concurrent test execution
public class RunResultGenerateHook {
private static Logger logger = Logging.getLogger(RunResultGenerateHook.class.getName());
private static boolean initialized = false;
private static File runResultsRootDir;
private static File captureRootDir;
private static int currentCaptureNo = 1;
private static RootMethodRunResult currentRunResult = null;
private static SrcTree srcTree;
// if called multiple times, just ignored
public static void initialize(String configFilePath) {
if (initialized) {
return;
}
logger.info("initialize");
final Config config;
try {
config = Config.generateFromYamlConfig(new File(configFilePath));
} catch (YamlConvertException e) {
throw new RuntimeException(e);
}
RunResultGenerateHook.runResultsRootDir
= CommonPath.runResultRootDir(config.getRootBaseReportIntermediateDataDir());
RunResultGenerateHook.captureRootDir
= CommonPath.inputCaptureRootDir(config.getRootBaseReportIntermediateDataDir());
final File srcTreeFile = CommonPath.srcTreeFile(config.getRootBaseReportIntermediateDataDir());
// load srcTree from already dumped srcTree YAML
srcTree = new SrcTree();
try {
srcTree.fromYamlObject(YamlUtils.load(srcTreeFile));
} catch (YamlConvertException e) {
throw new RuntimeException(e);
}
try {
srcTree.resolveKeyReference();
} catch (IllegalDataStructureException e) {
throw new RuntimeException(e);
}
if (!config.isRunTestOnly()) {
// set up shutdown hook which generates HTML report
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
HtmlReport report = new HtmlReport();
try {
report.generate(config.getRootBaseReportIntermediateDataDir(),
config.getRootBaseReportOutputDir());
} catch (IllegalDataStructureException e) {
throw new RuntimeException(e);
} catch (IllegalTestScriptException e) {
throw new RuntimeException(e);
}
}
});
}
initialized = true;
}
private static void initializedCheck() {
if (!initialized) {
throw new IllegalStateException("initialize first");
}
}
// initialize runResult information
public static void beforeRootMethodHook() {
logger.info("beforeRootMethodHook: start");
initializedCheck();
// initialize current captureNo and runResult
currentCaptureNo = 1;
currentRunResult = new RootMethodRunResult();
TestMethod rootMethod = StackLineUtils.getRootMethod(
srcTree.getRootMethodTable(), Thread.currentThread().getStackTrace());
if (rootMethod == null) {
throw new RuntimeException("implementation error");
}
currentRunResult.setRootMethodKey(rootMethod.getKey());
currentRunResult.setRootMethod(rootMethod);
logger.info("beforeRootMethodHook: end");
}
// set up runFailure information
public static void rootMethodErrorHook(Throwable e) {
initializedCheck();
RunFailure runFailure = new RunFailure();
runFailure.setMessage(e.getClass().getCanonicalName() + ": " + e.getLocalizedMessage());
runFailure.setStackTrace(ExceptionUtils.getStackTrace(e));
List stackLines = StackLineUtils.getStackLines(srcTree, e.getStackTrace());
for (StackLine stackLine : stackLines) {
runFailure.addStackLine(stackLine);
}
currentRunResult.addRunFailure(runFailure);
assert currentRunResult.getRootMethod() instanceof TestMethod;
TestMethod rootMethod = currentRunResult.getRootMethod();
captureScreenForStackLine(rootMethod, stackLines);
}
// write runResult to YAML file
public static void afterRootMethodHook() {
logger.info("afterRootMethodHook: start");
initializedCheck();
assert currentRunResult != null;
TestMethod rootMethod = currentRunResult.getRootMethod();
assert rootMethod.getTestClass() != null;
File runResultFile = new File(String.format("%s/%s/%s",
runResultsRootDir, rootMethod.getTestClass().getQualifiedName(),
rootMethod.getSimpleName()));
runResultFile.getParentFile().mkdirs();
YamlUtils.dump(currentRunResult.toYamlObject(), runResultFile);
// clear current captureNo and runResult
currentCaptureNo = -1;
currentRunResult = null;
logger.info("afterRootMethodHook: end");
}
public static void beforeRootCodeBodyHook(String classQualifiedName, String methodSimpleName,
int line, int actualInsertedLine) {
beforeCodeBodyHook(classQualifiedName, methodSimpleName, line, actualInsertedLine);
}
public static void beforeSubCodeBodyHook(String classQualifiedName, String methodSimpleName,
int line, int actualInsertedLine) {
beforeCodeBodyHook(classQualifiedName, methodSimpleName, line, actualInsertedLine);
}
private static void beforeCodeBodyHook(String classQualifiedName, String methodSimpleName,
int line, int actualInsertedLine) {
initializedCheck();
if (currentRunResult == null) {
return; // maybe called outside of the root method
}
logger.info("beforeCodeBodyHook: start: " + classQualifiedName + "." + methodSimpleName + ": " + line);
List stackLines = StackLineUtils.getStackLinesReplacingActualLine(
srcTree, Thread.currentThread().getStackTrace(),
classQualifiedName, methodSimpleName, actualInsertedLine, line);
if (stackLines.size() == 0) {
throw new RuntimeException("implementation error");
}
CodeLine thisCodeLine = stackLines.get(0).getMethod().getCodeBody().get(
stackLines.get(0).getCodeBodyIndex());
if (thisCodeLine.getCode() instanceof UnknownCode) {
logger.info("beforeCodeBodyHook: skip UnknownCode method: " + thisCodeLine.getCode().getOriginal());
return;
}
if (!canStepInCaptureTo(stackLines)) {
logger.info("beforeCodeBodyHook: skip no StepInCapture method");
return;
}
// screen capture
assert currentRunResult.getRootMethod() instanceof TestMethod;
TestMethod rootMethod = currentRunResult.getRootMethod();
File captureFile = captureScreen(rootMethod, stackLines);
if (captureFile != null) {
logger.info("beforeCodeBodyHook: end with capture " + captureFile.getName());
}
}
// returns null if not executed
private static File captureScreen(TestMethod rootMethod) {
byte[] screenData = AdapterContainer.globalInstance().captureScreen();
if (screenData == null) {
return null;
}
File captureFile = new File(String.format("%s/%s/%s/%03d.png",
captureRootDir, rootMethod.getTestClass().getQualifiedName(),
rootMethod.getSimpleName(), currentCaptureNo));
currentCaptureNo++;
captureFile.getParentFile().mkdirs();
FileOutputStream stream = null;
try {
stream = new FileOutputStream(captureFile);
stream.write(screenData);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(stream);
}
return captureFile;
}
// - returns null if fails to capture
// - try to capture even for UnknownCode and no stepInCapture line
// as long as code line exists in srcTree
private static File captureScreenForStackLine(
TestMethod rootMethod, List stackLines) {
File captureFile = captureScreen(rootMethod);
if (captureFile == null) {
return null;
}
LineScreenCapture capture = new LineScreenCapture();
capture.setPath(new File(captureFile.getAbsolutePath()));
capture.addAllStackLines(stackLines);
currentRunResult.addLineScreenCapture(capture);
return captureFile;
}
// returns screen capture file
// returns null if fail to capture
private static File captureScreen(TestMethod rootMethod, List stackLines) {
File captureFile = captureScreen(rootMethod);
if (captureFile == null) {
return null;
}
LineScreenCapture capture = new LineScreenCapture();
capture.setPath(new File(captureFile.getAbsolutePath()));
for (StackLine stackLine : stackLines) {
capture.addStackLine(stackLine);
}
currentRunResult.addLineScreenCapture(capture);
return captureFile;
}
// if method is called from not stepInCapture line, then returns false.
private static boolean canStepInCaptureTo(List stackLines) {
// stack bottom line ( = root line) is always regarded as stepIn true line
for (int i = 0; i < stackLines.size() - 1; i++) {
StackLine stackLine = stackLines.get(i);
CaptureStyle style = stackLine.getMethod().getCaptureStyle();
if (style != CaptureStyle.STEP_IN && style != CaptureStyle.STEP_IN_ONLY) {
return false;
}
}
return true;
}
}