/* * Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved. */ package jsystem.extensions.report.html; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Properties; import java.util.Stack; import java.util.logging.Level; import java.util.logging.Logger; import jsystem.extensions.report.html.summary.ContainerSummaryReport; import jsystem.framework.FrameworkOptions; import jsystem.framework.JSystemProperties; import jsystem.framework.common.CommonResources; import jsystem.framework.report.Reporter; import jsystem.framework.report.TestInfo; import jsystem.framework.scenario.JTestContainer; import jsystem.framework.scenario.ScenarioHelpers; import jsystem.framework.scenario.flow_control.AntFlowControl; import jsystem.framework.scenario.flow_control.AntForLoop; import jsystem.runner.loader.LoadersManager; import jsystem.utils.FileUtils; import jsystem.utils.StringUtils; public class HtmlWriter implements Serializable{ /** * */ private static final long serialVersionUID = -4635612042883816752L; public static String PACKAGE_FRAME = "packageFrame"; public static String TESTLIST_FRAME = "testList"; public static String TEST_FRAME = "testFrame"; private String resultDirectory = null; private String currentTestDir = null; private HtmlTestList packagesList = null; private HtmlTestList allTestsList = null; private HtmlTreeTestList scenarioHierarchy = null; private HtmlTestList allFailsList = null; private HtmlTestList lastPackageTestsList = null; protected HtmlTestList lastTestReportList = null; private String lastPackageName = null; private HashMap<String, HtmlTestList> testListsHash = new HashMap<String, HtmlTestList>(); public HtmlTestList mainFrame = null; protected Report lastDisabledReport = null; private NameGenerator generator; private static Logger log = Logger.getLogger(HtmlWriter.class.getName()); private int testCounter = 0; private Stack<HtmlTreeTestList> containersStack; private HtmlTreeTestList currentContainer; HashMap<String, Integer> rootScenario; private TestInfo lastTestInfo = null; public HtmlWriter(String resultDirectory) throws IOException { this.resultDirectory = resultDirectory; File resultDir = new File(resultDirectory); if (!resultDir.exists()) { resultDir.mkdirs(); } generator = new NameGenerator(); packagesList = new HtmlTestList(resultDirectory, null,"package_list"); packagesList.setFileName(generator.getName()); packagesList.setTarget(TESTLIST_FRAME); packagesList.setFastList(false); createIndexFile(); scenarioHierarchy = new HtmlTreeTestList(resultDirectory, null); scenarioHierarchy.setFileName(generator.getName()); scenarioHierarchy.setTarget(TESTLIST_FRAME); scenarioHierarchy.setTitle("Scenario Hierarchy:"); scenarioHierarchy.setFastList(false); scenarioHierarchy.setTreeRoot(true); scenarioHierarchy.doToFile(generator); allTestsList = new HtmlTestList(resultDirectory, null,"all_tests"); allTestsList.setFileName(generator.getName()); allTestsList.setTarget(TESTLIST_FRAME); allTestsList.setTitle("All Tests"); allTestsList.setFastList(false); allTestsList.doToFile(generator); allFailsList = new HtmlTestList(resultDirectory, null,"all_fails"); allFailsList.setFileName(generator.getName()); allFailsList.setTarget(TESTLIST_FRAME); allFailsList.setTitle("All Fails"); allFailsList.setFastList(false); allFailsList.doToFile(generator); TestReport tmpReport = new TestReport("ScenarioHierarchy:", null, Reporter.PASS, false, false, resultDirectory); scenarioHierarchy.addReport(tmpReport); tmpReport = new TestReport("All Tests:", null, Reporter.PASS, false, false, resultDirectory); allTestsList.addReport(tmpReport); tmpReport = new TestReport("All Fails:", null, Reporter.PASS, false, false, resultDirectory); allFailsList.addReport(tmpReport); tmpReport.toFile(generator); currentContainer = scenarioHierarchy; try { addResourceFiles(); } catch (Exception e) { log.warning("Failed adding needed html files for sceario Hierarchy"); } rootScenario = new HashMap<String, Integer>(); containersStack = new Stack<HtmlTreeTestList>(); legacyPackageList(); //note method doesn't do anything if FrameworkOptions.HTML_PACKAGE_LIST is not set. updatePackageListFromPropsFile(); updateTestDirectoryFile(""); scenarioHierarchy.setContainerSummaryReport(new ContainerSummaryReport(new File(resultDirectory,generator.getName()),"root")); } /** * Creates the legacy package list, which includes the following links: * 1. summary report * 2. scenario hierarchy * 3. allTests * 4. allFails * 5. tests by package */ private void legacyPackageList() { packagesList.addReport(new Link("summary.html", "Summary report", TEST_FRAME)); packagesList.addReport(new LineBreak()); TestReport tmpReport = new TestReport("Tests packages:", null, Reporter.PASS, false, false, resultDirectory); packagesList.addReport(tmpReport); packagesList.addReport(scenarioHierarchy); packagesList.addReport(allTestsList); packagesList.addReport(allFailsList); try { packagesList.doToFile(generator); }catch (Exception e) { log.fine("Failed updating package list file." + e.getMessage()); } } /** * Updates package list area with links taken from * a property file. * Name of property file is taken from jsystem.properties (FrameworkOptions.HTML_PACKAGE_LIST) * For each property in the properties file, a link is added, the key is link text, and property value * is the url referred by the link. */ private void updatePackageListFromPropsFile() { try { String propsFileName = JSystemProperties.getInstance().getPreference(FrameworkOptions.HTML_PACKAGE_LIST); if (StringUtils.isEmpty(propsFileName)){ return; } packagesList.removeAllReports(); Properties p = FileUtils.loadPropertiesFromFile(propsFileName); Enumeration<Object> e = p.keys(); while (e.hasMoreElements()){ String k = e.nextElement().toString(); String v = p.getProperty(k); packagesList.addReport(new Link(v, k,TEST_FRAME)); } packagesList.toFile(generator); }catch (Exception e1) { log.fine("Failed updating package list file." + e1.getMessage()); } } /** * add javascript, css and images * @throws Exception */ private void addResourceFiles(){ String sourceFolder = "jsystem/extensions/report/html/resources/"; String[] fileNames = {"bullet.gif","minus.gif","plus.gif","mktree.js","mktree.css","scenario.gif"}; String destinationFolder = resultDirectory+"/"; for (String fileName : fileNames){ copyResource(sourceFolder+fileName, new File(destinationFolder,fileName)); } //copy additional css String cssPathList[] = StringUtils.split(JSystemProperties.getInstance().getPreferenceOrDefault(FrameworkOptions.HTML_CSS_PATH),";"); for (String cssPath:cssPathList){ copyResource(cssPath,new File(destinationFolder,FileUtils.getFileNameWithoutFullPath(cssPath))); } } private void copyResource(String sourcePath,File destination) { ClassLoader loader = LoadersManager.getInstance().getLoader(); InputStream is = loader.getResourceAsStream(sourcePath); try { FileUtils.saveInputStreamToFile(is,destination); } catch (Exception e) { log.warning("Failed copying File "+sourcePath+" needed to represent Html Hierarchy"); }finally{ try { is.close(); } catch (IOException e) { log.warning("failed closing input stream"); } } } public synchronized String newTestStart(TestInfo testinfo) throws IOException { testCounter++; String testName; String tName = StringUtils.getClassName(testinfo.className); String actualName = testinfo.methodName == null ? tName : tName + "." + testinfo.methodName; String packageName = StringUtils.getPackageName(testinfo.className); /* * If meaningful name exist will use it */ if(testinfo.meaningfulName != null){ testName = testinfo.meaningfulName; } else { testName = actualName; } /* * If comment exits will add it to the current name */ if (testinfo.comment != null){ testName = testName + " - " + testinfo.comment; } if (testinfo.className == null) { return null; } String usedName = null; if (testinfo.count == 1) { usedName = testCounter + " " + testName; actualName = testCounter + " "+actualName; } else { usedName = testCounter + " " + testName + "(" + testinfo.count + ")"; actualName = testCounter + " " + actualName + "(" + testinfo.count + ")"; } currentTestDir = "test_" + testCounter; updateTestDirectoryFile(currentTestDir); // The test is from a new package the list should be created/get. if (!packageName.equals(lastPackageName)) { lastPackageName = packageName; lastPackageTestsList = (HtmlTestList) testListsHash .get(lastPackageName); if (lastPackageTestsList == null) { // create a new package list lastPackageTestsList = new HtmlTestList(resultDirectory, null,"package_list"); lastPackageTestsList.setFileName(generator.getName()); lastPackageTestsList.setTarget(TESTLIST_FRAME); lastPackageTestsList.setTitle(packageName); lastPackageTestsList.setFastList(false); lastPackageTestsList.doToFile(generator); TestReport tmpReprot = new TestReport("Tests in package " + lastPackageName + ":", null, Reporter.PASS, false, false, resultDirectory); lastPackageTestsList.addReport(tmpReprot); testListsHash.put(lastPackageName, lastPackageTestsList); packagesList.addReport(lastPackageTestsList); tmpReprot.toFile(generator); } else { lastPackageTestsList = (HtmlTestList) testListsHash .get(lastPackageName); } } lastTestReportList = new HtmlTestList(resultDirectory, null,"test_list"); lastTestReportList.setFastList(true); String testFileName = generator.getName(); lastTestReportList.setFileName(testFileName); lastTestReportList.setTarget(TEST_FRAME); lastTestReportList.setTitle(usedName); lastTestReportList.setFileName(testFileName); if (testinfo.parameters != null) { lastTestReportList.setAlt(testinfo.parameters); } currentContainer.addReport(lastTestReportList); allTestsList.addReport(lastTestReportList); lastPackageTestsList.addReport(lastTestReportList); lastTestInfo = testinfo; return testFileName; } /** * Update a file which holds current test directory name * * @param directory the current test directory name */ private void updateTestDirectoryFile(String directory){ Properties p = new Properties(); if (!StringUtils.isEmpty(directory)){ try { FileUtils.addPropertyToFile(CommonResources.TEST_INNER_TEMP_FILENAME, CommonResources.TEST_DIR_KEY, directory); }catch (Exception e) { log.log(Level.WARNING,"Failed updating tmp properties",e); } } } /** * signal that a container started, create a new html element and add to stack * * @param container the container object */ public void startContainer(JTestContainer container){ boolean isRoot = false; String title = ""; if (container.isRoot()){ String name = ScenarioHelpers.removeScenarioHeader(container.getTestName()); title = name; Integer index = (Integer) rootScenario.get(name); int rootIndex = 1; if (index != null) { rootIndex = index.intValue() + 1; title += "(" + rootIndex + ")"; } rootScenario.put(name, rootIndex); isRoot = true; }else if (container instanceof AntForLoop) { title = ((AntForLoop)container).getTestName(0); }else if (container instanceof AntFlowControl){ title = container.getTestName(); }else{ // Scenario title = ScenarioHelpers.removeScenarioHeader(container.getTestName()); } addHierarchyList(title,isRoot); } /** * signal that a container ended, switch to the higher container * * @param container the container object */ public void endContainer(JTestContainer container){ HtmlTreeTestList tmp = currentContainer; currentContainer = containersStack.pop(); if (container.isHiddenInHTML() && container.isSuccess()){ currentContainer.removeReport(tmp); updateHierarchy(); } } private void updateHierarchy() { try { scenarioHierarchy.setStatusChange(true); scenarioHierarchy.toFile(generator); }catch (Exception e){ throw new RuntimeException("Failed saving report file"); } } /** * signal a loop start with a given count * * @param loop loop object * @param count loop number */ public void startLoop(AntForLoop loop, int count){ addHierarchyList(loop.getTestName(count),false); } /** * signal a loop ended with a given count * * @param loop loop object * @param count loop number */ public void endLoop(AntForLoop loop, int count){ currentContainer = containersStack.pop(); } /** * add an html list object and update hierarchy * * @param title the title of the html object */ private void addHierarchyList(String title,boolean isRoot){ HtmlTreeTestList tmpContainer = new HtmlTreeTestList(resultDirectory,null); tmpContainer.setRootScenario(isRoot); tmpContainer.setTarget(TEST_FRAME); tmpContainer.setTitle(title); tmpContainer.setContainerSummaryReport(new ContainerSummaryReport(new File(resultDirectory,generator.getName()),title)); containersStack.push(currentContainer); currentContainer.addReport(tmpContainer); currentContainer = tmpContainer; } /** * * @return current main frame file name */ public String getCurrentMainFrameFileName() { return lastTestReportList.getFileName(); } /** * Create new HtmlTestList with specific file name * * @param fileName * @return a list of test report */ public HtmlTestList createNewLevelReportList(String fileName) { String title = lastTestReportList.getTitle(); String dir = lastTestReportList.getLogDirectory(); lastTestReportList = new HtmlTestList(dir, currentTestDir,"level_list"); lastTestReportList.setFastList(true); lastTestReportList.setFileName(fileName); lastTestReportList.setTarget(TEST_FRAME); lastTestReportList.setTitle(title); return lastTestReportList; } public void backToMainFrame() { if (mainFrame != null){ lastTestReportList = mainFrame; } } public HtmlTestList getLastTestReportList() { return lastTestReportList; } public synchronized void endTest(long runningTime, boolean failed) throws IOException { if (lastTestInfo != null && lastTestInfo.isHiddenInHTML && !failed && lastTestReportList != null){ currentContainer.removeReport(lastTestReportList); allTestsList.removeReport(lastTestReportList); allTestsList.setStatusChange(true); allTestsList.toFile(generator); lastPackageTestsList.removeReport(lastTestReportList); lastPackageTestsList.setStatusChange(true); lastPackageTestsList.toFile(generator); deleteListFiles(lastTestReportList); lastTestReportList = null; } if (failed && lastTestReportList != null) { allFailsList.addReport(lastTestReportList); allFailsList.toFile(generator); } updateHierarchy(); updatePackageListFromPropsFile(); } public synchronized void addReport(TestReport testReport) throws IOException { if (lastTestReportList == null) { return; } testReport.setDirectory(currentTestDir); testReport.setLogDirectory(resultDirectory); testReport.setTarget(TEST_FRAME); lastTestReportList.addReport(testReport); testReport.toFile(generator); } /** * Create basic index.html file * * @throws IOException */ private void createIndexFile() throws IOException { StringBuilder sb = new StringBuilder(""); sb.append("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n"). append("<HTML>\n").append("<HEAD>\n").append("<TITLE>\n"). append("JSystem results\n").append("</TITLE>\n").append("</HEAD>\n"). append("<FRAMESET cols=\"30%,70%\">\n"). append("<FRAMESET rows=\"30%,70%\">\n"). append(" <FRAME src=\"report1.html\" name=\"").append(PACKAGE_FRAME). append("\">\n").append(" <FRAME src=\"report2.html\" name=\""). append(TESTLIST_FRAME).append("\">\n").append("</FRAMESET>\n"). append("<FRAME src=\"summary.html\" name=\"").append(TEST_FRAME).append("\">\n"). append("</FRAMESET>\n").append("</HTML>\n"); FileWriter file = new FileWriter(resultDirectory + File.separatorChar + "index.html"); file.write(sb.toString()); file.close(); } /** * Return the last test report status. * * @return Test status, true for success and false for fail. */ public int getLastTestReportStatus() { return lastTestReportList.isSuccess(); } /** * signal a run ended */ public void runEnded(){ containersStack.clear(); currentContainer = scenarioHierarchy; } public void resetCurrentContainer(){ currentContainer = scenarioHierarchy; } public void flush() throws Exception { scenarioHierarchy.setStatusChange(true); scenarioHierarchy.doToFile(generator); packagesList.doToFile(generator); allTestsList.doToFile(generator); allFailsList.doToFile(generator); Collection<HtmlTestList> lists = testListsHash.values(); Iterator<HtmlTestList> iter = lists.iterator(); while(iter.hasNext()){ iter.next().doToFile(generator); } } private void deleteListFiles(HtmlTestList list) { File folder = new File(list.getLogDirectory(),currentTestDir); File testHtmlFile = new File(list.getLogDirectory(),list.fileName); if (!testHtmlFile.delete()) { log.info("Failed deleteing hidden test file " + list.fileName); } FileUtils.deltree(folder); } public void setContainerProperties(int ancestorLevel, String key,String value) { HtmlTreeTestList list = currentContainer; if (ancestorLevel > 0){ int pos = 0; if (ancestorLevel < containersStack.size()) { pos = containersStack.size() - ancestorLevel; } list = containersStack.get(pos); } list.getContainerSummaryReport().setProperty(key, value); } }