1 package org.collectionspace.services.IntegrationTests.xmlreplay;
3 import org.collectionspace.services.client.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;
12 import java.util.ArrayList;
13 import java.util.List;
15 /** Format a report based on XmlReplay ServiceResult object from a test group.
18 public class XmlReplayReport {
19 public static final String INCLUDES_DIR = "_includes";
21 protected static final String HTML_PAGE_END = "</body></html>";
22 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>";
24 protected static final String HTML_TEST_START = "<div class='TESTCASE'>";
25 protected static final String HTML_TEST_END = "</div>";
27 protected static final String GROUP_START = "<div class='TESTGROUP'>";
28 protected static final String GROUP_END = "</div>";
30 protected static final String RUNINFO_START = "<div class='RUNINFO'>";
31 protected static final String RUNINFO_END = "</div>";
34 protected static final String DIV_END = "</div>";
36 protected static final String PRE_START = "<pre class='SUMMARY'>";
37 protected static final String PRE_END = "</pre>";
38 protected static final String BR = "<br />\r\n";
40 protected static final String DETAIL_START = "<table border='1' class='DETAIL_TABLE'><tr><td>\r\n";
41 protected static final String DETAIL_LINESEP = "</td></tr>\r\n<tr><td>";
42 protected static final String DETAIL_END = "</td></tr></table>";
44 private static final String SP = " ";
46 public XmlReplayReport(String reportsDir){
47 this.reportsDir = reportsDir;
50 private String reportsDir = "";
51 public String getReportsDir(){
55 protected static String formatCollapse(String myDivID, String linkText){
56 return "<a href='javascript:;' onmousedown=\"toggleDiv('"+myDivID+"');\">"+linkText+"</a>"
58 + "<div ID='"+myDivID+"' class='PAYLOAD' style='display:none'>";
62 private StringBuffer header = new StringBuffer();
63 private StringBuffer buffer = new StringBuffer();
64 private String runInfo = "";
66 public String getPage(String basedir){
67 return formatPageStart(basedir)
68 +"<div class='REPORTTIME'>XmlReplay run "+Tools.nowLocale()+"</div>"
72 +getTOC("").toString()
78 public String getTOC(String reportName){
79 StringBuffer tocBuffer = new StringBuffer();
81 if (Tools.notBlank(reportName)){
82 // We are generating a TOC for an index.html file that references other report files.
83 tocBuffer.append(this.header.toString());
85 // 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..
86 tocBuffer.append(BR).append(TOPLINKS).append(BR);
88 for (TOC toc: tocList){
89 tocBuffer.append(BR+"<a href='"+reportName+"#TOC"+toc.tocID+"'>"+toc.testID+"</a> "+ toc.detail);
91 tocBuffer.append(BR+BR);
92 return tocBuffer.toString();
95 public void addRunInfo(String text){
96 this.runInfo = RUNINFO_START+text+RUNINFO_END;
97 //addText(this.runInfo);
100 /** Call this method to insert arbitrary HTML in your report file, at the point after the last call to addTestResult() or addTestGroup(). */
101 public void addText(String text){
105 public void addTestGroup(String groupID, String controlFile){
106 header.append(GROUP_START);
107 header.append(lbl("Test Group")).append(groupID).append(SP).append(lbl("Control File")).append(controlFile);
108 header.append(GROUP_END);
111 private int divID = 0;
113 public void addTestResult(ServiceResult serviceResult){
114 buffer.append(HTML_TEST_START);
116 buffer.append(formatSummary(serviceResult, tocID));
117 buffer.append(formatPayloads(serviceResult, tocID));
118 buffer.append(HTML_TEST_END);
121 public static class TOC {
123 public String testID;
124 public String detail;
126 private List<TOC> tocList = new ArrayList<TOC>();
128 public static String formatPageStart(String xmlReplayBaseDir){
129 String script = FileTools.readFile(xmlReplayBaseDir, INCLUDES_DIR+"/reports-include.js");
130 String style = FileTools.readFile(xmlReplayBaseDir, INCLUDES_DIR+"/reports-include.css");
131 return "<html><head><script type='text/javascript'>\r\n"
133 +"\r\n</script>\r\n<style>\r\n"
135 +"\r\n</style></head><body>";
138 public File saveReport(String xmlReplayBaseDir, String reportsDir, String reportName) {
140 File resultFile = FileTools.saveFile(reportsDir, reportName, this.getPage(xmlReplayBaseDir), true);
141 if (resultFile!=null) {
142 String resultFileName = resultFile.getCanonicalPath();
143 //System.out.println("XmlReplay summary reports saved to directory: "+resultFile.getParent());
144 System.out.println("XmlReplay summary report: "+resultFileName);
147 } catch (Exception e){
148 System.out.println("ERROR saving XmlReplay report in basedir: "+reportsDir+" reportName: "+reportName+" error: "+e);
153 //public static String getReportsDir(String basename){
154 // return Tools.glue(basename,"/","TEST-REPORTS");
157 /** @param localMasterFilename should be a local filename for the index of each xmlReplay master control file, e.g. objectexit.xml
158 * so what gets written to disk will be something like index.objectexit.xml.html . The actual filename will be available from
159 * the returned File object if successful.
160 * @return File if successful, else returns null.
162 public static File saveIndexForMaster(String xmlReplayBaseDir, String reportsDir, String localMasterFilename, List<String> reportsList){
163 String masterFilename = "index."+localMasterFilename+".html";
165 StringBuffer sb = new StringBuffer(formatPageStart(xmlReplayBaseDir));
166 String dateStr = Tools.nowLocale();
167 sb.append("<div class='REPORTTIME'>XmlReplay run "+dateStr+" master: "+localMasterFilename+"</div>");
168 for (String oneToc: reportsList){
172 sb.append(HTML_PAGE_END);
174 return FileTools.saveFile(reportsDir,masterFilename, sb.toString(), false);
175 } catch (Exception e){
176 System.out.println("ERROR saving XmlReplay report index: in xmlReplayBaseDir: "+reportsDir+"localMasterFilename: "+localMasterFilename+" masterFilename: "+masterFilename+" list: "+reportsList+" error: "+e);
181 protected String formatSummary(ServiceResult serviceResult, int tocID){
184 toc.testID = serviceResult.testID;
185 toc.detail = (serviceResult.gotExpectedResult() ? ok("SUCCESS") : red("FAILURE") );
188 StringBuffer fb = new StringBuffer();
189 fb.append("<a name='TOC"+tocID+"'></a>");
190 fb.append(detail(serviceResult, false, false, DETAIL_START, DETAIL_LINESEP, DETAIL_END, tocID));
191 return fb.toString();
194 protected String formatPayloads(ServiceResult serviceResult, int tocID){
195 StringBuffer fb = new StringBuffer();
197 appendPayload(fb, serviceResult.requestPayload, "REQUEST", "REQUEST" + tocID);
198 appendPayload(fb, serviceResult.requestPayloadsRaw, "REQUEST (RAW)", "REQUESTRAW" + tocID);
199 appendPayload(fb, serviceResult.result, "RESPONSE", "RESPONSE" + tocID);
200 appendPayload(fb, serviceResult.expectedContentExpanded, "EXPECTED", "EXPECTED" + tocID);
203 return fb.toString();
206 protected void appendPayload( StringBuffer fb , String payload, String title, String theDivID){
207 if (Tools.notBlank(payload)){
208 //fb.append(BR+title+":"+BR);
210 String pretty = prettyPrint(payload);
213 fb.append(formatCollapse(theDivID, title)); //starts a div.
214 fb.append(PRE_START);
215 fb.append(escape(pretty));
217 fb.append(DIV_END); //ends that div.
218 } catch (Exception e){
219 String error = "<font color='red'>ERROR:</font> "+payload;
221 fb.append(BR).append(BR);
226 private String escape(String source){
228 return Tools.searchAndReplace(source, "<", "<");
229 } catch (Exception e){
230 return "ERROR escaping requestPayload"+e;
234 private String prettyPrint(String rawXml) throws Exception {
235 Document document = DocumentHelper.parseText(rawXml);
236 return XmlTools.prettyPrint(document, " ");
239 private static final String LINE = "<hr />\r\n";
240 private static final String CRLF = "<br />\r\n";
242 protected String red(String label){
243 return "<span class='ERROR'>"+label+"</span> ";
245 protected String ok(String label){
246 return "<span class='OK'>"+label+"</span> ";
248 protected String lbl(String label){
249 return "<span class='LABEL'>"+label+":</span> ";
251 public String detail(ServiceResult s, boolean includePayloads, boolean includePartSummary, String start, String linesep, String end, int tocID){
252 String partSummary = s.partsSummary(includePartSummary);
254 + ( s.gotExpectedResult() ? lbl("SUCCESS") : "<font color='red'><b>FAILURE</b></font>" )
255 + SP + ( Tools.notEmpty(s.testID) ? s.testID : "" )
257 +s.method+ SP +"<a href='"+s.fullURL+"'>"+s.fullURL+"</a>" +linesep
258 + s.responseCode+ SP +lbl("gotExpected")+s.gotExpectedResult() +linesep
259 + (Tools.notBlank(s.failureReason) ? s.failureReason +linesep : "" )
260 + ( (s.expectedCodes.size()>0) ? lbl("expectedCodes")+s.expectedCodes+linesep : "" )
261 //+ ( Tools.notEmpty(s.testGroupID) ? "testGroupID:"+s.testGroupID+linesep : "" )
262 //THIS WORKS, BUT IS VERBOSE: + ( Tools.notEmpty(s.fromTestID) ? "fromTestID:"+s.fromTestID+linesep : "" )
263 + ( Tools.notEmpty(s.responseMessage) ? lbl("msg")+s.responseMessage+linesep : "" )
264 +lbl("auth")+s.auth +linesep
265 + ( Tools.notEmpty(s.deleteURL) ? lbl("deleteURL")+s.deleteURL+linesep : "" )
266 + ( Tools.notEmpty(s.location) ? lbl("location.CSID")+s.location+linesep : "" )
267 + ( Tools.notEmpty(s.error) ? "ERROR:"+s.error +linesep : "" )
268 + ((includePartSummary && Tools.notBlank(partSummary)) ? lbl("part summary")+partSummary +linesep : "")
269 + ( includePayloads && Tools.notBlank(s.requestPayload) ? LINE+lbl("requestPayload")+LINE+CRLF+s.requestPayload+LINE : "" )
270 + ( includePayloads && Tools.notBlank(s.result) ? LINE+lbl("result")+LINE+CRLF+s.result : "" )