1 package org.collectionspace.services.IntegrationTests.xmlreplay;
3 import org.collectionspace.services.common.XmlTools;
4 import org.collectionspace.services.common.api.FileTools;
5 import org.collectionspace.services.common.api.Tools;
6 import org.dom4j.Document;
7 import org.dom4j.DocumentHelper;
9 import javax.swing.text.Style;
11 import java.util.ArrayList;
12 import java.util.List;
14 /** Format a report based on XmlReplay ServiceResult object from a test group.
17 public class XmlReplayReport {
18 protected static final String HTML_PAGE_END = "</body></html>";
19 protected static final String TOPLINKS = "<a class='TOPLINKS' href='javascript:openAll();'>Show All Payloads</a>" + "<a class='TOPLINKS' href='javascript:closeAll();'>Hide All Payloads</a>";
21 protected static final String HTML_TEST_START = "<div class='TESTCASE'>";
22 protected static final String HTML_TEST_END = "</div>";
24 protected static final String GROUP_START = "<div class='TESTGROUP'>";
25 protected static final String GROUP_END = "</div>";
27 protected static final String RUNINFO_START = "<div class='RUNINFO'>";
28 protected static final String RUNINFO_END = "</div>";
31 protected static final String DIV_END = "</div>";
33 protected static final String PRE_START = "<pre class='SUMMARY'>";
34 protected static final String PRE_END = "</pre>";
35 protected static final String BR = "<br />\r\n";
37 protected static final String DETAIL_START = "<table border='1' class='DETAIL_TABLE'><tr><td>\r\n";
38 protected static final String DETAIL_LINESEP = "</td></tr>\r\n<tr><td>";
39 protected static final String DETAIL_END = "</td></tr></table>";
41 private static final String SP = " ";
44 protected static String formatCollapse(String myDivID, String linkText){
45 return "<a href='javascript:;' onmousedown=\"toggleDiv('"+myDivID+"');\">"+linkText+"</a>"
47 + "<div ID='"+myDivID+"' class='PAYLOAD' style='display:none'>";
51 private StringBuffer header = new StringBuffer();
52 private StringBuffer buffer = new StringBuffer();
53 private String runInfo = "";
55 public String getPage(String basedir){
56 return formatPageStart(basedir)
57 +"<div class='REPORTTIME'>XmlReplay run "+Tools.nowLocale()+"</div>"
61 +getTOC("").toString()
67 public String getTOC(String reportName){
68 StringBuffer tocBuffer = new StringBuffer();
70 if (Tools.notBlank(reportName)){
71 // We are generating a TOC for an index.html file that references other report files.
72 tocBuffer.append(this.header.toString());
74 // We are generating a single report file, so all links are relative to this file, and we should have the TOPLINKS which allow things like showAllPayloads..
75 tocBuffer.append(BR).append(TOPLINKS).append(BR);
77 for (TOC toc: tocList){
78 tocBuffer.append(BR+"<a href='"+reportName+"#TOC"+toc.tocID+"'>"+toc.testID+"</a> "+ toc.detail);
80 tocBuffer.append(BR+BR);
81 return tocBuffer.toString();
84 public void addRunInfo(String text){
85 this.runInfo = RUNINFO_START+text+RUNINFO_END;
86 //addText(this.runInfo);
89 /** Call this method to insert arbitrary HTML in your report file, at the point after the last call to addTestResult() or addTestGroup(). */
90 public void addText(String text){
94 public void addTestGroup(String groupID, String controlFile){
95 header.append(GROUP_START);
96 header.append(lbl("Test Group")).append(groupID).append(SP).append(lbl("Control File")).append(controlFile);
97 header.append(GROUP_END);
100 private int divID = 0;
102 public void addTestResult(ServiceResult serviceResult){
103 buffer.append(HTML_TEST_START);
105 buffer.append(formatSummary(serviceResult, tocID));
106 buffer.append(formatPayloads(serviceResult, tocID));
107 buffer.append(HTML_TEST_END);
110 public static class TOC {
112 public String testID;
113 public String detail;
115 private List<TOC> tocList = new ArrayList<TOC>();
117 public static String formatPageStart(String xmlReplayBaseDir){
118 String script = FileTools.readFile(xmlReplayBaseDir, "TEST-REPORTS/reports-include.js");
119 String style = FileTools.readFile(xmlReplayBaseDir, "TEST-REPORTS/reports-include.css");
120 return "<html><head><script type='text/javascript'>\r\n"
122 +"\r\n</script>\r\n<style>\r\n"
124 +"\r\n</style></head><body>";
127 public File saveReport(String xmlReplayBaseDir, String reportName) {
129 File resultFile = FileTools.saveFile(getReportsDir(xmlReplayBaseDir), reportName, this.getPage(xmlReplayBaseDir), true);
130 if (resultFile!=null) {
131 String resultFileName = resultFile.getCanonicalPath();
132 //System.out.println("XmlReplay summary reports saved to directory: "+resultFile.getParent());
133 System.out.println("XmlReplay summary report: "+resultFileName);
136 } catch (Exception e){
137 System.out.println("ERROR saving XmlReplay report in basedir: "+xmlReplayBaseDir+" reportName: "+reportName+" error: "+e);
142 public static String getReportsDir(String basename){
143 return Tools.glue(basename,"/","TEST-REPORTS");
146 /** @param localMasterFilename should be a local filename for the index of each xmlReplay master control file, e.g. objectexit.xml
147 * so what gets written to disk will be something like index.objectexit.xml.html . The actual filename will be available from
148 * the returned File object if successful.
149 * @return File if successful, else returns null.
151 public static File saveIndexForMaster(String xmlReplayBaseDir, String localMasterFilename, List<String> reportsList){
152 String masterFilename = "index."+localMasterFilename+".html";
154 StringBuffer sb = new StringBuffer(formatPageStart(xmlReplayBaseDir));
155 String dateStr = Tools.nowLocale();
156 sb.append("<div class='REPORTTIME'>XmlReplay run "+dateStr+" master: "+localMasterFilename+"</div>");
157 for (String oneToc: reportsList){
161 sb.append(HTML_PAGE_END);
163 return FileTools.saveFile(getReportsDir(xmlReplayBaseDir),masterFilename, sb.toString(), false);
164 } catch (Exception e){
165 System.out.println("ERROR saving XmlReplay report index: in xmlReplayBaseDir: "+xmlReplayBaseDir+"localMasterFilename: "+localMasterFilename+" masterFilename: "+masterFilename+" list: "+reportsList+" error: "+e);
170 protected String formatSummary(ServiceResult serviceResult, int tocID){
173 toc.testID = serviceResult.testID;
174 toc.detail = (serviceResult.gotExpectedResult() ? ok("SUCCESS") : red("FAILURE") );
177 StringBuffer fb = new StringBuffer();
178 fb.append("<a name='TOC"+tocID+"'></a>");
179 fb.append(detail(serviceResult, false, false, DETAIL_START, DETAIL_LINESEP, DETAIL_END, tocID));
180 return fb.toString();
183 protected String formatPayloads(ServiceResult serviceResult, int tocID){
184 StringBuffer fb = new StringBuffer();
186 appendPayload(fb, serviceResult.requestPayload, "REQUEST", "REQUEST" + tocID);
187 appendPayload(fb, serviceResult.requestPayloadsRaw, "REQUEST (RAW)", "REQUESTRAW" + tocID);
188 appendPayload(fb, serviceResult.result, "RESPONSE", "RESPONSE" + tocID);
191 return fb.toString();
194 protected void appendPayload( StringBuffer fb , String payload, String title, String theDivID){
195 if (Tools.notBlank(payload)){
196 //fb.append(BR+title+":"+BR);
198 String pretty = prettyPrint(payload);
201 fb.append(formatCollapse(theDivID, title)); //starts a div.
202 fb.append(PRE_START);
203 fb.append(escape(pretty));
205 fb.append(DIV_END); //ends that div.
206 } catch (Exception e){
207 String error = "<font color='red'>ERROR:</font> "+payload;
209 fb.append(BR).append(BR);
214 private String escape(String source){
216 return Tools.searchAndReplace(source, "<", "<");
217 } catch (Exception e){
218 return "ERROR escaping requestPayload"+e;
222 private String prettyPrint(String rawXml) throws Exception {
223 Document document = DocumentHelper.parseText(rawXml);
224 return XmlTools.prettyPrint(document, " ");
227 private static final String LINE = "<hr />\r\n";
228 private static final String CRLF = "<br />\r\n";
230 protected String red(String label){
231 return "<span class='ERROR'>"+label+"</span> ";
233 protected String ok(String label){
234 return "<span class='OK'>"+label+"</span> ";
236 protected String lbl(String label){
237 return "<span class='LABEL'>"+label+":</span> ";
239 public String detail(ServiceResult s, boolean includePayloads, boolean includePartSummary, String start, String linesep, String end, int tocID){
240 String partSummary = s.partsSummary(includePartSummary);
242 + ( s.gotExpectedResult() ? lbl("SUCCESS") : "<font color='red'><b>FAILURE</b></font>" )
243 + SP + ( Tools.notEmpty(s.testID) ? s.testID : "" )
245 +s.method+ SP +"<a href='"+s.fullURL+"'>"+s.fullURL+"</a>" +linesep
246 + s.responseCode+ SP +lbl("gotExpected")+s.gotExpectedResult() +linesep
247 + (Tools.notBlank(s.failureReason) ? s.failureReason +linesep : "" )
248 + ( (s.expectedCodes.size()>0) ? lbl("expectedCodes")+s.expectedCodes+linesep : "" )
249 //+ ( Tools.notEmpty(s.testGroupID) ? "testGroupID:"+s.testGroupID+linesep : "" )
250 //THIS WORKS, BUT IS VERBOSE: + ( Tools.notEmpty(s.fromTestID) ? "fromTestID:"+s.fromTestID+linesep : "" )
251 + ( Tools.notEmpty(s.responseMessage) ? lbl("msg")+s.responseMessage+linesep : "" )
252 +lbl("auth")+s.auth +linesep
253 + ( Tools.notEmpty(s.deleteURL) ? lbl("deleteURL")+s.deleteURL+linesep : "" )
254 + ( Tools.notEmpty(s.location) ? lbl("location.CSID")+s.location+linesep : "" )
255 + ( Tools.notEmpty(s.error) ? "ERROR:"+s.error +linesep : "" )
256 + ((includePartSummary && Tools.notBlank(partSummary)) ? lbl("part summary")+partSummary +linesep : "")
257 + ( includePayloads && Tools.notBlank(s.requestPayload) ? LINE+lbl("requestPayload")+LINE+CRLF+s.requestPayload+LINE : "" )
258 + ( includePayloads && Tools.notBlank(s.result) ? LINE+lbl("result")+LINE+CRLF+s.result : "" )