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 public static final String INCLUDES_DIR = "_includes";
20 protected static final String HTML_PAGE_END = "</body></html>";
21 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>";
23 protected static final String HTML_TEST_START = "<div class='TESTCASE'>";
24 protected static final String HTML_TEST_END = "</div>";
26 protected static final String GROUP_START = "<div class='TESTGROUP'>";
27 protected static final String GROUP_END = "</div>";
29 protected static final String RUNINFO_START = "<div class='RUNINFO'>";
30 protected static final String RUNINFO_END = "</div>";
33 protected static final String DIV_END = "</div>";
35 protected static final String PRE_START = "<pre class='SUMMARY'>";
36 protected static final String PRE_END = "</pre>";
37 protected static final String BR = "<br />\r\n";
39 protected static final String DETAIL_START = "<table border='1' class='DETAIL_TABLE'><tr><td>\r\n";
40 protected static final String DETAIL_LINESEP = "</td></tr>\r\n<tr><td>";
41 protected static final String DETAIL_END = "</td></tr></table>";
43 private static final String SP = " ";
45 public XmlReplayReport(String reportsDir){
46 this.reportsDir = reportsDir;
49 private String reportsDir = "";
50 public String getReportsDir(){
54 protected static String formatCollapse(String myDivID, String linkText){
55 return "<a href='javascript:;' onmousedown=\"toggleDiv('"+myDivID+"');\">"+linkText+"</a>"
57 + "<div ID='"+myDivID+"' class='PAYLOAD' style='display:none'>";
61 private StringBuffer header = new StringBuffer();
62 private StringBuffer buffer = new StringBuffer();
63 private String runInfo = "";
65 public String getPage(String basedir){
66 return formatPageStart(basedir)
67 +"<div class='REPORTTIME'>XmlReplay run "+Tools.nowLocale()+"</div>"
71 +getTOC("").toString()
77 public String getTOC(String reportName){
78 StringBuffer tocBuffer = new StringBuffer();
80 if (Tools.notBlank(reportName)){
81 // We are generating a TOC for an index.html file that references other report files.
82 tocBuffer.append(this.header.toString());
84 // 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..
85 tocBuffer.append(BR).append(TOPLINKS).append(BR);
87 for (TOC toc: tocList){
88 tocBuffer.append(BR+"<a href='"+reportName+"#TOC"+toc.tocID+"'>"+toc.testID+"</a> "+ toc.detail);
90 tocBuffer.append(BR+BR);
91 return tocBuffer.toString();
94 public void addRunInfo(String text){
95 this.runInfo = RUNINFO_START+text+RUNINFO_END;
96 //addText(this.runInfo);
99 /** Call this method to insert arbitrary HTML in your report file, at the point after the last call to addTestResult() or addTestGroup(). */
100 public void addText(String text){
104 public void addTestGroup(String groupID, String controlFile){
105 header.append(GROUP_START);
106 header.append(lbl("Test Group")).append(groupID).append(SP).append(lbl("Control File")).append(controlFile);
107 header.append(GROUP_END);
110 private int divID = 0;
112 public void addTestResult(ServiceResult serviceResult){
113 buffer.append(HTML_TEST_START);
115 buffer.append(formatSummary(serviceResult, tocID));
116 buffer.append(formatPayloads(serviceResult, tocID));
117 buffer.append(HTML_TEST_END);
120 public static class TOC {
122 public String testID;
123 public String detail;
125 private List<TOC> tocList = new ArrayList<TOC>();
127 public static String formatPageStart(String xmlReplayBaseDir){
128 String script = FileTools.readFile(xmlReplayBaseDir, INCLUDES_DIR+"/reports-include.js");
129 String style = FileTools.readFile(xmlReplayBaseDir, INCLUDES_DIR+"/reports-include.css");
130 return "<html><head><script type='text/javascript'>\r\n"
132 +"\r\n</script>\r\n<style>\r\n"
134 +"\r\n</style></head><body>";
137 public File saveReport(String xmlReplayBaseDir, String reportsDir, String reportName) {
139 File resultFile = FileTools.saveFile(reportsDir, reportName, this.getPage(xmlReplayBaseDir), true);
140 if (resultFile!=null) {
141 String resultFileName = resultFile.getCanonicalPath();
142 //System.out.println("XmlReplay summary reports saved to directory: "+resultFile.getParent());
143 System.out.println("XmlReplay summary report: "+resultFileName);
146 } catch (Exception e){
147 System.out.println("ERROR saving XmlReplay report in basedir: "+reportsDir+" reportName: "+reportName+" error: "+e);
152 //public static String getReportsDir(String basename){
153 // return Tools.glue(basename,"/","TEST-REPORTS");
156 /** @param localMasterFilename should be a local filename for the index of each xmlReplay master control file, e.g. objectexit.xml
157 * so what gets written to disk will be something like index.objectexit.xml.html . The actual filename will be available from
158 * the returned File object if successful.
159 * @return File if successful, else returns null.
161 public static File saveIndexForMaster(String xmlReplayBaseDir, String reportsDir, String localMasterFilename, List<String> reportsList){
162 String masterFilename = "index."+localMasterFilename+".html";
164 StringBuffer sb = new StringBuffer(formatPageStart(xmlReplayBaseDir));
165 String dateStr = Tools.nowLocale();
166 sb.append("<div class='REPORTTIME'>XmlReplay run "+dateStr+" master: "+localMasterFilename+"</div>");
167 for (String oneToc: reportsList){
171 sb.append(HTML_PAGE_END);
173 return FileTools.saveFile(reportsDir,masterFilename, sb.toString(), false);
174 } catch (Exception e){
175 System.out.println("ERROR saving XmlReplay report index: in xmlReplayBaseDir: "+reportsDir+"localMasterFilename: "+localMasterFilename+" masterFilename: "+masterFilename+" list: "+reportsList+" error: "+e);
180 protected String formatSummary(ServiceResult serviceResult, int tocID){
183 toc.testID = serviceResult.testID;
184 toc.detail = (serviceResult.gotExpectedResult() ? ok("SUCCESS") : red("FAILURE") );
187 StringBuffer fb = new StringBuffer();
188 fb.append("<a name='TOC"+tocID+"'></a>");
189 fb.append(detail(serviceResult, false, false, DETAIL_START, DETAIL_LINESEP, DETAIL_END, tocID));
190 return fb.toString();
193 protected String formatPayloads(ServiceResult serviceResult, int tocID){
194 StringBuffer fb = new StringBuffer();
196 appendPayload(fb, serviceResult.requestPayload, "REQUEST", "REQUEST" + tocID);
197 appendPayload(fb, serviceResult.requestPayloadsRaw, "REQUEST (RAW)", "REQUESTRAW" + tocID);
198 appendPayload(fb, serviceResult.result, "RESPONSE", "RESPONSE" + tocID);
199 appendPayload(fb, serviceResult.expectedContentExpanded, "EXPECTED", "EXPECTED" + tocID);
202 return fb.toString();
205 protected void appendPayload( StringBuffer fb , String payload, String title, String theDivID){
206 if (Tools.notBlank(payload)){
207 //fb.append(BR+title+":"+BR);
209 String pretty = prettyPrint(payload);
212 fb.append(formatCollapse(theDivID, title)); //starts a div.
213 fb.append(PRE_START);
214 fb.append(escape(pretty));
216 fb.append(DIV_END); //ends that div.
217 } catch (Exception e){
218 String error = "<font color='red'>ERROR:</font> "+payload;
220 fb.append(BR).append(BR);
225 private String escape(String source){
227 return Tools.searchAndReplace(source, "<", "<");
228 } catch (Exception e){
229 return "ERROR escaping requestPayload"+e;
233 private String prettyPrint(String rawXml) throws Exception {
234 Document document = DocumentHelper.parseText(rawXml);
235 return XmlTools.prettyPrint(document, " ");
238 private static final String LINE = "<hr />\r\n";
239 private static final String CRLF = "<br />\r\n";
241 protected String red(String label){
242 return "<span class='ERROR'>"+label+"</span> ";
244 protected String ok(String label){
245 return "<span class='OK'>"+label+"</span> ";
247 protected String lbl(String label){
248 return "<span class='LABEL'>"+label+":</span> ";
250 public String detail(ServiceResult s, boolean includePayloads, boolean includePartSummary, String start, String linesep, String end, int tocID){
251 String partSummary = s.partsSummary(includePartSummary);
253 + ( s.gotExpectedResult() ? lbl("SUCCESS") : "<font color='red'><b>FAILURE</b></font>" )
254 + SP + ( Tools.notEmpty(s.testID) ? s.testID : "" )
256 +s.method+ SP +"<a href='"+s.fullURL+"'>"+s.fullURL+"</a>" +linesep
257 + s.responseCode+ SP +lbl("gotExpected")+s.gotExpectedResult() +linesep
258 + (Tools.notBlank(s.failureReason) ? s.failureReason +linesep : "" )
259 + ( (s.expectedCodes.size()>0) ? lbl("expectedCodes")+s.expectedCodes+linesep : "" )
260 //+ ( Tools.notEmpty(s.testGroupID) ? "testGroupID:"+s.testGroupID+linesep : "" )
261 //THIS WORKS, BUT IS VERBOSE: + ( Tools.notEmpty(s.fromTestID) ? "fromTestID:"+s.fromTestID+linesep : "" )
262 + ( Tools.notEmpty(s.responseMessage) ? lbl("msg")+s.responseMessage+linesep : "" )
263 +lbl("auth")+s.auth +linesep
264 + ( Tools.notEmpty(s.deleteURL) ? lbl("deleteURL")+s.deleteURL+linesep : "" )
265 + ( Tools.notEmpty(s.location) ? lbl("location.CSID")+s.location+linesep : "" )
266 + ( Tools.notEmpty(s.error) ? "ERROR:"+s.error +linesep : "" )
267 + ((includePartSummary && Tools.notBlank(partSummary)) ? lbl("part summary")+partSummary +linesep : "")
268 + ( includePayloads && Tools.notBlank(s.requestPayload) ? LINE+lbl("requestPayload")+LINE+CRLF+s.requestPayload+LINE : "" )
269 + ( includePayloads && Tools.notBlank(s.result) ? LINE+lbl("result")+LINE+CRLF+s.result : "" )