/*********************************************************************************** * * Copyright (c) 2015 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.spy.testcases; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.script.ScriptException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.baczkowicz.spy.common.generated.ScriptDetails; import pl.baczkowicz.spy.files.FileUtils; import pl.baczkowicz.spy.scripts.BaseScriptManager; import pl.baczkowicz.spy.scripts.ScriptRunningState; import pl.baczkowicz.spy.utils.ThreadingUtils; import pl.baczkowicz.spy.utils.TimeUtils; public class TestCaseManager { public static String GET_INFO_METHOD = "getInfo"; public static SimpleDateFormat testCaseFileSdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); public static SimpleDateFormat testCasesFileSdf = new SimpleDateFormat("yyyyMMdd"); private final static Logger logger = LoggerFactory.getLogger(TestCaseManager.class); protected final BaseScriptManager scriptManager; protected Map<String, TestCase> testCases = new HashMap<>(); protected int running = 0; private TestCaseOptions options = new TestCaseOptions(); public TestCaseManager(final BaseScriptManager scriptManager) { this.scriptManager = scriptManager; } public TestCase addTestCase(final File scriptFile) { if (!scriptFile.exists()) { logger.error("Script file " + scriptFile.getPath() + " does not exist"); // TODO: Throw an exception instead? return null; } logger.info("Adding " + scriptFile.getName() + " with parent " + scriptFile.getParent()); final ScriptDetails scriptDetails = new ScriptDetails(); scriptDetails.setFile(scriptFile.getAbsolutePath()); scriptDetails.setRepeat(false); final TestCase testCase = new TestCase(); scriptManager.createFileBasedScript(testCase, scriptFile, scriptDetails); try { scriptManager.runScript(testCase, false); testCase.setInfo((TestCaseInfo) scriptManager.invokeFunction(testCase, GET_INFO_METHOD)); int stepNumber = 1; for (final String step : testCase.getInfo().getSteps()) { testCase.getSteps().add(new TestCaseStep(String.valueOf(stepNumber), step, TestCaseStatus.NOT_RUN, "")); stepNumber++; } logger.info(testCase.getInfo().getName() + " " + Arrays.asList(testCase.getInfo().getSteps())); } catch (ScriptException | NoSuchMethodException e) { logger.error("Cannot read test case", e); } // Override name if (testCase.getInfo() != null && testCase.getInfo().getName() != null) { testCase.setName(testCase.getInfo().getName()); } else { testCase.setName(scriptFile.getParentFile().getName()); } testCases.put(testCase.getScriptId(), testCase); return testCase; } public void loadTestCases(final String testCaseLocation) { final List<File> scripts = FileUtils.getDirectoriesWithFile(testCaseLocation, "tc.*.js"); for (final File scriptFile : scripts) { addTestCase(scriptFile); } } private TestCaseStepResult runTestCaseSteps(final TestCase testCase) { TestCaseStepResult lastResult = null; while (testCase.getCurrentStep() < testCase.getSteps().size() && testCase.getStatus().equals(ScriptRunningState.RUNNING)) { final TestCaseStep step = testCase.getSteps().get(testCase.getCurrentStep()); testCase.setLastUpdated(TimeUtils.DATE_WITH_SECONDS_SDF.format(new Date())); step.setStatus(TestCaseStatus.IN_PROGRESS); try { final TestCaseStepResult result = (TestCaseStepResult) scriptManager.invokeFunction(testCase, "step" + step.getStepNumber()); lastResult = result; if (result == null) { continue; } step.setStatus(result.getStatus()); step.setExecutionInfo(result.getInfo()); if (TestCaseStatus.IN_PROGRESS.equals(result.getStatus())) { // Add a copy of the step, as status and exec info might change later testCase.getTestCaseResult().getStepResults().add(new TestCaseStep(step)); try { Thread.sleep(options.getStepInterval()); } catch (InterruptedException e) { break; } } // If not in progress any more, move to next else { testCase.getTestCaseResult().getStepResults().add(step); testCase.setCurrentStep(testCase.getCurrentStep() + 1); } } catch (NoSuchMethodException e) { step.setStatus(TestCaseStatus.ERROR); logger.error("Step execution error for step " + step.getStepNumber(), e); } catch (ScriptException e) { step.setStatus(TestCaseStatus.FAILED); logger.error("Step execution failure for step " + step.getStepNumber(), e); } } return lastResult; } public void runAllTestCaseMethods(final TestCase testCase) { running++; testCase.setCurrentStep(0); // Before if (!scriptManager.invokeBefore(testCase)) { testCase.setStatusAndNotify(ScriptRunningState.FAILED); } // Test steps TestCaseStepResult lastResult = runTestCaseSteps(testCase); // After if (!scriptManager.invokeAfter(testCase)) { testCase.setStatusAndNotify(ScriptRunningState.FAILED); } final TestCaseStepResult testCaseStatus = lastResult; if (testCase.getStatus().equals(ScriptRunningState.STOPPED)) { testCase.setTestCaseStatus(TestCaseStatus.SKIPPED); } else { testCase.setTestCaseStatus(testCaseStatus.getStatus()); testCase.setStatusAndNotify(ScriptRunningState.FINISHED); } testCase.getTestCaseResult().setInfo(testCase.getInfo()); testCase.getTestCaseResult().setResult(testCase.getTestCaseStatus()); testCase.setLastUpdated(TimeUtils.DATE_WITH_SECONDS_SDF.format(new Date())); running--; logger.info("Test case \"{}\" ended with result: {}", testCase.getName(), testCaseStatus.getStatus()); if (options.isAutoExport()) { final String parentDir = testCase.getScriptFile().getParent() + System.getProperty("file.separator"); exportTestCaseResultAsCSV(testCase, new File(parentDir + "result_" + testCaseFileSdf.format(new Date()) + "_" + testCaseStatus.getStatus() + ".csv")); } } public void runTestCase(final TestCase testCase, final Map<String, Object> args) { testCase.setStatusAndNotify(ScriptRunningState.RUNNING); testCase.setTestCaseStatus(TestCaseStatus.IN_PROGRESS); // Set test case args if (args != null) { scriptManager.setVariable(testCase, "args", args); } // Clear last run for this test case for (final TestCaseStep step : testCase.getSteps()) { step.setStatus(TestCaseStatus.NOT_RUN); step.setExecutionInfo(""); } runAllTestCaseMethods(testCase); } public TestCaseResult addAndRunTestCase(final String testCaseLocation, final Map<String, Object> args) { final TestCase testCase = addTestCase(new File(testCaseLocation)); // TODO: add protection against missing/invalid files runTestCase(testCase, args); return testCase.getTestCaseResult(); } public void runAllTestCases() { running = testCases.size(); new Thread(new Runnable() { @Override public void run() { ThreadingUtils.logThreadStarting("runAllTestCases"); for (final TestCase testCase : testCases.values()) { runTestCase(testCase, null); running--; } ThreadingUtils.logThreadEnding(); } }).start(); } public void stopTestCase(final TestCase testCase) { testCase.setStatusAndNotify(ScriptRunningState.STOPPED); final TestCaseStep step = testCase.getSteps().get(testCase.getCurrentStep()); step.setStatus(TestCaseStatus.SKIPPED); } public int getTotalCount() { return testCases.size(); } public Collection<TestCase> getTestCases() { return testCases.values(); } // *** Export methods *** public void exportTestCaseResultAsCSV(final TestCase testCase, final File selectedFile) { logger.info("Saving test case results to " + selectedFile.getAbsolutePath()); try { BufferedWriter out = new BufferedWriter(new FileWriter(selectedFile)); out.write( //"Time, " + "Step" + ", " + "\"" + "Description" + "\"" + ", " + "Status" + ", " + "\"" + "Info" + "\""); out.newLine(); for (TestCaseStep step : testCase.getSteps()) { out.write( //step. step.getStepNumber() + ", " + "\"" + step.getDescription() + "\"" + ", " + step.getStatus() + ", " + "\"" + step.getExecutionInfo() + "\""); out.newLine(); } out.close(); } catch (IOException e) { logger.error("Cannot write to file", e); } } public void exportTestCasesResultsAsCSV(final File selectedFile) { logger.info("Saving test cases results to " + selectedFile.getAbsolutePath()); try { BufferedWriter out = new BufferedWriter(new FileWriter(selectedFile)); out.write( "\"" + "Test case" + "\"" + ", " + "\"" + "Last updated" + "\"" + ", " + "\"" + "Status"); out.newLine(); for (TestCase testCase : getTestCases()) { out.write( "\"" + testCase.getName() + "\"" + ", " + "\"" + testCase.getLastUpdated() + "\"" + ", " + "\"" + testCase.getTestCaseStatus() + "\"" ); out.newLine(); } out.close(); } catch (IOException e) { logger.error("Cannot write to file", e); } } public boolean areTestCasesStillRunning() { return running > 0; } public void setOptions(final TestCaseOptions options) { this.options = options; } public TestCaseOptions getOptions() { return this.options; } }