\r
import org.apache.commons.io.FileUtils;\r
import org.apache.commons.jexl2.JexlEngine;\r
+import org.collectionspace.services.common.api.FileTools;\r
import org.collectionspace.services.common.api.Tools;\r
+import org.collectionspace.services.common.service.ServiceBindingType;\r
import org.dom4j.*;\r
import org.dom4j.io.SAXReader;\r
\r
public XmlReplay(String basedir){\r
this.basedir = basedir;\r
this.serviceResultsMap = createResultsMap();\r
+ this.reportsList = new ArrayList<String>();\r
}\r
\r
public static final String DEFAULT_CONTROL = "xml-replay-control.xml";\r
return new HashMap<String, ServiceResult>();\r
}\r
\r
+ private List<String> reportsList;\r
+ public List<String> getReportsList(){\r
+ return reportsList;\r
+ }\r
\r
public String toString(){\r
return "XmlReplay{"+this.basedir+", "+this.defaultAuthsMap+", "+this.dump+'}';\r
\r
// ============== METHODS ===========================================================\r
\r
+ /** Optional information method: call this method after instantiating this class using the constructor XmlReplay(String), which sets the basedir. Then you\r
+ * pass in your relative masterFilename to that basedir to this method, which will return true if the file is readable, valid xml, etc.\r
+ * Do this in preference to just seeing if File.exists(), because there are rules to finding the file relative to the maven test dir, yada, yada.\r
+ * This method makes it easy to have a development test file that you don't check in, so that dev tests can be missing gracefully, etc.\r
+ */\r
+ public boolean masterConfigFileExists(String masterFilename){\r
+ try {\r
+ org.dom4j.Document doc = openMasterConfigFile(masterFilename);\r
+ if (doc == null){\r
+ return false;\r
+ }\r
+ return true;\r
+ } catch (Throwable t){\r
+ return false;\r
+ }\r
+ }\r
+\r
public org.dom4j.Document openMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
- org.dom4j.Document document = getDocument(Tools.glue(basedir, "/", masterFilename)); //will check full path first, then checks relative to PWD.\r
+ String fullPath = Tools.glue(basedir, "/", masterFilename);\r
+ File f = new File(fullPath);\r
+ if (!f.exists()){\r
+ return null;\r
+ }\r
+ org.dom4j.Document document = getDocument(fullPath); //will check full path first, then checks relative to PWD.\r
if (document == null){\r
throw new FileNotFoundException("XmlReplay master control file ("+masterFilename+") not found in basedir: "+basedir+". Exiting test.");\r
}\r
*/\r
public org.dom4j.Document readOptionsFromMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
org.dom4j.Document document = openMasterConfigFile(masterFilename);\r
+ if (document == null){\r
+ throw new FileNotFoundException(masterFilename);\r
+ }\r
protoHostPort = document.selectSingleNode("/xmlReplayMaster/protoHostPort").getText().trim();\r
AuthsMap authsMap = readAuths(document);\r
setDefaultAuthsMap(authsMap);\r
} else {\r
document = openMasterConfigFile(masterFilename);\r
}\r
+ if (document==null){\r
+ throw new FileNotFoundException(masterFilename);\r
+ }\r
String controlFile, testGroup, test;\r
List<Node> runNodes;\r
runNodes = document.selectNodes("/xmlReplayMaster/run");\r
//Now run *that* instance.\r
List<ServiceResult> results = replay.runTests(testGroup, test);\r
list.add(results);\r
+ this.reportsList.addAll(replay.getReportsList()); //Add all the reports from the inner replay, to our master replay's reportsList, to generate the index.html file.\r
}\r
+ StringBuffer sb = new StringBuffer(XmlReplayReport.HTML_PAGE_START);\r
+ String dateStr = Tools.nowLocale();\r
+ sb.append("<div class='REPORTTIME'>XmlReplay run "+dateStr+" master: "+masterFilename+"</div>");\r
+ for (String oneToc: this.reportsList){\r
+ sb.append(oneToc).append("<hr />");\r
+ }\r
+ sb.append(XmlReplayReport.HTML_PAGE_END);\r
+ FileTools.saveFile(getReportsDir(this.basedir),"index."+masterFilename+".html", sb.toString(), false);\r
return list;\r
}\r
\r
this.autoDeletePOSTS,\r
dump,\r
this.protoHostPort,\r
- this.defaultAuthsMap);\r
+ this.defaultAuthsMap,\r
+ this.reportsList);\r
return result;\r
}\r
\r
this.autoDeletePOSTS,\r
dump,\r
this.protoHostPort,\r
- this.defaultAuthsMap);\r
+ this.defaultAuthsMap,\r
+ this.reportsList);\r
if (result.size()>1){\r
throw new IndexOutOfBoundsException("Multiple ("+result.size()+") tests with ID='"+testID+"' were found within test group '"+testGroupID+"', but there should only be one test per ID attribute.");\r
}\r
document = reader.read(xmlFileName);\r
} catch (DocumentException e) {\r
System.out.println("ERROR reading document: "+e);\r
- e.printStackTrace();\r
+ //e.printStackTrace();\r
}\r
return document;\r
}\r
boolean param_autoDeletePOSTS,\r
Dump dump,\r
String protoHostPortParam,\r
- AuthsMap defaultAuths)\r
+ AuthsMap defaultAuths,\r
+ List<String> reportsList)\r
throws Exception {\r
//Internally, we maintain two collections of ServiceResult:\r
// the first is the return value of this method.\r
// the second is the serviceResultsMap, which is used for keeping track of CSIDs created by POSTs, for later reference by DELETE, etc.\r
List<ServiceResult> results = new ArrayList<ServiceResult>();\r
\r
+ XmlReplayReport report = new XmlReplayReport();\r
+\r
String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName);\r
org.dom4j.Document document;\r
document = getDocument(controlFile); //will check full path first, then checks relative to PWD.\r
} else {\r
authsMapINFO = "Using AuthsMap from control file: "+authsMap;\r
}\r
- System.out.println("========================================================================"\r
+\r
+ report.addTestGroup(testGroupID, controlFileName); //controlFileName is just the short name, without the full path.\r
+ String xmlReplayHeader = "========================================================================"\r
+"\r\nXmlReplay running:"\r
+"\r\n controlFile: "+ (new File(controlFile).getCanonicalPath())\r
+"\r\n protoHostPort: "+protoHostPort\r
+"\r\n param_autoDeletePOSTS: "+param_autoDeletePOSTS\r
+"\r\n Dump info: "+dump\r
+"\r\n========================================================================"\r
- +"\r\n");\r
+ +"\r\n";\r
+ report.addRunInfo(xmlReplayHeader);\r
+\r
+ System.out.println(xmlReplayHeader);\r
\r
String autoDeletePOSTS = "";\r
List<Node> testgroupNodes;\r
String serviceResultRow = serviceResult.dump(dump.dumpServiceResult, hasError)+"; time:"+(System.currentTimeMillis()-startTime);\r
String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";\r
\r
+ report.addTestResult(serviceResult);\r
+\r
if ( (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed)\r
|| (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.full) ){\r
System.out.println("\r\n#---------------------#");\r
autoDelete(serviceResultsMap, "default");\r
}\r
}\r
+\r
+ //=== Now spit out the HTML report file ===\r
+ File m = new File(controlFileName);\r
+ String localName = m.getName();//don't instantiate, just use File to extract file name without directory.\r
+ String reportName = localName+'-'+testGroupID+".html";\r
+ File resultFile = FileTools.saveFile(getReportsDir(xmlReplayBaseDir), reportName, report.getPage(), true);\r
+ if (resultFile!=null) {\r
+ System.out.println("XmlReplay summary reports saved to directory: "+resultFile.getParent());\r
+ System.out.println("XmlReplay summary report: "+resultFile.getCanonicalPath());\r
+ String toc = report.getTOC(reportName);\r
+ reportsList.add(toc);\r
+ }\r
+ //================================\r
+\r
return results;\r
}\r
\r
+ //todo: move from xmlReplayBaseDir to "target/xmlReplayReports" dir.\r
+ public static String getReportsDir(String basename){\r
+ return Tools.glue(basename,"/","TEST-REPORTS");\r
+ }\r
+\r
//======================== MAIN ===================================================================\r
\r
private static Options createOptions() {\r
} else {\r
Dump dump = getDumpConfig();\r
dump.payloads = Tools.isTrue(dumpResults);\r
- runXmlReplayFile(xmlReplayBaseDirResolved, controlFilename, testGroupID, testID, createResultsMap(), bAutoDeletePOSTS, dump, "", null);\r
+ List<String> reportsList = new ArrayList<String>();\r
+ runXmlReplayFile(xmlReplayBaseDirResolved, controlFilename, testGroupID, testID, createResultsMap(), bAutoDeletePOSTS, dump, "", null, reportsList);\r
+ System.out.println("DEPRECATED: reportsList is generated, but not dumped: "+reportsList.toString());\r
}\r
} catch (ParseException exp) {\r
// oops, something went wrong\r
--- /dev/null
+package org.collectionspace.services.IntegrationTests.xmlreplay;
+
+import org.collectionspace.services.common.XmlTools;
+import org.collectionspace.services.common.api.Tools;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Format a report based on XmlReplay ServiceResult object from a test group.
+ * @author laramie
+ */
+public class XmlReplayReport {
+ public static final String HTML_PAGE_START = "<html><head><script type='text/javascript' src='reports-include.js'></script>"
+ +"<LINK rel='stylesheet' href='reports-include.css'></LINK></head><body>";
+ protected static final String HTML_PAGE_END = "</body></html>";
+ 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>";
+
+ protected static final String HTML_TEST_START = "<div class='TESTCASE'>";
+ protected static final String HTML_TEST_END = "</div>";
+
+ protected static final String GROUP_START = "<div class='TESTGROUP'>";
+ protected static final String GROUP_END = "</div>";
+
+ protected static final String RUNINFO_START = "<div class='RUNINFO'>";
+ protected static final String RUNINFO_END = "</div>";
+
+
+ protected static final String DIV_END = "</div>";
+
+ protected static final String PRE_START = "<pre class='SUMMARY'>";
+ protected static final String PRE_END = "</pre>";
+ protected static final String BR = "<br />\r\n";
+
+ protected static final String DETAIL_START = "<table border='1' class='DETAIL_TABLE'><tr><td>\r\n";
+ protected static final String DETAIL_LINESEP = "</td></tr>\r\n<tr><td>";
+ protected static final String DETAIL_END = "</td></tr></table>";
+
+ private static final String SP = " ";
+
+
+ protected static String formatCollapse(String myDivID, String linkText){
+ return "<a href='javascript:;' onmousedown=\"toggleDiv('"+myDivID+"');\">"+linkText+"</a>"
+ + BR
+ + "<div ID='"+myDivID+"' class='PAYLOAD' style='display:none'>";
+ }
+
+
+ private StringBuffer header = new StringBuffer();
+ private StringBuffer buffer = new StringBuffer();
+ private String runInfo = "";
+ //private StringBuffer toc = new StringBuffer();
+
+ public String getPage(){
+ return HTML_PAGE_START
+ +"<div class='REPORTTIME'>XmlReplay run "+Tools.nowLocale()+"</div>"
+ +header.toString()
+ +this.runInfo
+ +BR
+ +getTOC("").toString()
+ +BR
+ +buffer.toString()
+ +HTML_PAGE_END;
+ }
+
+ public String getTOC(String reportName){
+ StringBuffer tocBuffer = new StringBuffer();
+
+ if (Tools.notBlank(reportName)){
+ // We are generating an index.html file.
+ tocBuffer.append(this.header.toString());
+ }
+ tocBuffer.append(BR).append(TOPLINKS).append(BR);
+ for (TOC toc: tocList){
+ tocBuffer.append(BR+"<a href='"+reportName+"#TOC"+toc.tocID+"'>"+toc.testID+"</a> "+ toc.detail);
+ }
+ tocBuffer.append(BR+BR);
+ return tocBuffer.toString();
+ }
+
+ public void addRunInfo(String text){
+ this.runInfo = RUNINFO_START+text+RUNINFO_END;
+ //addText(this.runInfo);
+ }
+
+
+ public void addText(String text){
+ buffer.append(text);
+ }
+ public void addTestGroup(String groupID, String controlFile){
+ header.append(GROUP_START);
+ header.append(lbl("Test Group")).append(groupID).append(SP).append(lbl("Control File")).append(controlFile);
+ header.append(GROUP_END);
+ }
+
+ private int divID = 0;
+
+ public void addTestResult(ServiceResult serviceResult){
+ buffer.append(HTML_TEST_START);
+ int tocID = ++divID;
+ buffer.append(formatSummary(serviceResult, tocID));
+ buffer.append(formatPayloads(serviceResult, tocID));
+ buffer.append(HTML_TEST_END);
+ }
+
+ public static class TOC {
+ public int tocID;
+ public String testID;
+ public String detail;
+ }
+ private List<TOC> tocList = new ArrayList<TOC>();
+
+ protected String formatSummary(ServiceResult serviceResult, int tocID){
+ TOC toc = new TOC();
+ toc.tocID = tocID;
+ toc.testID = serviceResult.testID;
+ toc.detail = (serviceResult.gotExpectedResult() ? ok("SUCCESS") : red("FAILURE") );
+ tocList.add(toc);
+
+ StringBuffer fb = new StringBuffer();
+ fb.append("<a name='TOC"+tocID+"'></a>");
+ fb.append(detail(serviceResult, false, false, DETAIL_START, DETAIL_LINESEP, DETAIL_END, tocID));
+ return fb.toString();
+ }
+
+ protected String formatPayloads(ServiceResult serviceResult, int tocID){
+ StringBuffer fb = new StringBuffer();
+ fb.append(BR);
+ appendPayload(fb, serviceResult.requestPayload, "REQUEST", "REQUEST" + tocID);
+ appendPayload(fb, serviceResult.requestPayloadsRaw, "REQUEST (RAW)", "REQUESTRAW" + tocID);
+ appendPayload(fb, serviceResult.result, "RESPONSE", "RESPONSE" + tocID);
+ fb.append(BR);
+
+ return fb.toString();
+ }
+
+ protected void appendPayload( StringBuffer fb , String payload, String title, String theDivID){
+ if (Tools.notBlank(payload)){
+ //fb.append(BR+title+":"+BR);
+ try {
+ String pretty = prettyPrint(payload);
+
+
+ fb.append(formatCollapse(theDivID, title)); //starts a div.
+ fb.append(PRE_START);
+ fb.append(escape(pretty));
+ fb.append(PRE_END);
+ fb.append(DIV_END); //ends that div.
+ } catch (Exception e){
+ String error = "<font color='red'>ERROR:</font> "+payload;
+ fb.append(error);
+ fb.append(BR).append(BR);
+ }
+ }
+ }
+
+ private String escape(String source){
+ try {
+ return Tools.searchAndReplace(source, "<", "<");
+ } catch (Exception e){
+ return "ERROR escaping requestPayload"+e;
+ }
+ }
+
+ private String prettyPrint(String rawXml) throws Exception {
+ Document document = DocumentHelper.parseText(rawXml);
+ return XmlTools.prettyPrint(document, " ");
+ }
+
+ private static final String LINE = "<hr />\r\n";
+ private static final String CRLF = "<br />\r\n";
+
+ protected String red(String label){
+ return "<span class='ERROR'>"+label+"</span> ";
+ }
+ protected String ok(String label){
+ return "<span class='OK'>"+label+"</span> ";
+ }
+ protected String lbl(String label){
+ return "<span class='LABEL'>"+label+":</span> ";
+ }
+ public String detail(ServiceResult s, boolean includePayloads, boolean includePartSummary, String start, String linesep, String end, int tocID){
+ String partSummary = s.partsSummary(includePartSummary);
+ String res = start
+ + ( s.gotExpectedResult() ? lbl("SUCCESS") : "<font color='red'><b>FAILURE</b></font>" )
+ + SP + ( Tools.notEmpty(s.testID) ? s.testID : "" )
+ + SP +linesep
+ +s.method+ SP +"<a href='"+s.fullURL+"'>"+s.fullURL+"</a>" +linesep
+ + s.responseCode+ SP +lbl("gotExpected")+s.gotExpectedResult() +linesep
+ + (Tools.notBlank(s.failureReason) ? s.failureReason +linesep : "" )
+ + ( (s.expectedCodes.size()>0) ? lbl("expectedCodes")+s.expectedCodes+linesep : "" )
+ //+ ( Tools.notEmpty(s.testGroupID) ? "testGroupID:"+s.testGroupID+linesep : "" )
+ //THIS WORKS, BUT IS VERBOSE: + ( Tools.notEmpty(s.fromTestID) ? "fromTestID:"+s.fromTestID+linesep : "" )
+ + ( Tools.notEmpty(s.responseMessage) ? lbl("msg")+s.responseMessage+linesep : "" )
+ +lbl("auth")+s.auth +linesep
+ + ( Tools.notEmpty(s.deleteURL) ? lbl("deleteURL")+s.deleteURL+linesep : "" )
+ + ( Tools.notEmpty(s.location) ? lbl("location.CSID")+s.location+linesep : "" )
+ + ( Tools.notEmpty(s.error) ? "ERROR:"+s.error +linesep : "" )
+ + ((includePartSummary && Tools.notBlank(partSummary)) ? lbl("part summary")+partSummary +linesep : "")
+ + ( includePayloads && Tools.notBlank(s.requestPayload) ? LINE+lbl("requestPayload")+LINE+CRLF+s.requestPayload+LINE : "" )
+ + ( includePayloads && Tools.notBlank(s.result) ? LINE+lbl("result")+LINE+CRLF+s.result : "" )
+ +end;
+ return res;
+ }
+
+}
throws Exception {\r
byte[] b = FileUtils.readFileToByteArray(new File(fileName));\r
String xmlString = new String(b);\r
+ String contentRaw = xmlString;\r
xmlString = evalStruct.eval(xmlString, evalStruct.serviceResultsMap, vars, evalStruct.jexl, evalStruct.jc);\r
String urlString = protoHostPort+uri;\r
- String contentRaw = xmlString;\r
return doPOST_PUT(urlString, xmlString, contentRaw, BOUNDARY, method, contentType, authForTest, fromTestID); //method is POST or PUT.\r
}\r
\r
System.out.println("Using default masterFile: "+masterFile);
}
XmlReplay replay = createXmlReplayUsingIntegrationTestsModule("..");
- List<List<ServiceResult>> list = replay.runMaster(masterFile);
- logTestForGroup(list, "XmlReplayMasterTest");
+ if (replay.masterConfigFileExists(masterFile)){ // CSPACE-4027
+ List<List<ServiceResult>> list = replay.runMaster(masterFile);
+ logTestForGroup(list, "XmlReplayMasterTest");
+ } else {
+ System.out.println("XmlReplayDevTest skipping local dev test "+masterFile+" because it doesn't exist in "+replay.getBaseDir()+". This is expected behavior on a production installation.");
+ }
+
//used for testing load.
// bigLoop(masterFile);
--- /dev/null
+.REPORTTIME {background-color: white; color: black; text-decoration: none; font-family: Tahoma; font-size:10pt;
+ border-width: 1px; border-color: black; border-style: solid; padding: 8px; margin-bottom: 20px;}
+.TESTGROUP {background-color: lightblue; color: black; text-decoration: none; font-family: Tahoma; font-size:14pt;}
+.RUNINFO {background-color: lightyellow; color: black; text-decoration: none; font-family: "Courier New", Courier, mono;
+ font-size:10pt; white-space: pre; padding: 5px; }
+.LABEL {font-size: 9pt; color: gray;}
+.OK {font-size: 9pt; color: green;}
+.ERROR {font-size: 14pt; color: red; font-weight: bold;}
+.PAYLOAD {background-color: white;}
+.TOPLINKS { font-size: 14pt; padding: 20px;}
+.DETAIL_TABLE {
+ font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 12pt;
+ width:100%;
+ border-collapse:collapse;
+ border:2px solid #98bf21;
+ padding: 7px;
+}
+
+body {
+ background-color: #87cefa;
+ font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
+ font-size: 10pt;
+ }
+td {background-color: #e0ffff;}
--- /dev/null
+function toggleDiv(divid){
+ if(document.getElementById(divid).style.display == 'none'){
+ document.getElementById(divid).style.display = 'block';
+ }else{
+ document.getElementById(divid).style.display = 'none';
+ }
+}
+
+// usage: <a href="javascript:openAll();">openAll</a>
+function openAll( ) {
+ var divs = document.getElementsByTagName("div");
+ for ( t = 0; t < divs.length; ++t ) {
+ var td = divs[t];
+ if (td.className == "PAYLOAD"){
+ td.style.display = "block";
+ }
+ }
+}
+
+// usage: <a href="javascript:openAll();">closeAll</a>
+function closeAll( ){
+ var divs = document.getElementsByTagName("div");
+ for ( t = 0; t < divs.length; ++t ) {
+ var td = divs[t];
+ if (td.className == "PAYLOAD"){
+ td.style.display = "none";
+ }
+ }
+}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>\r
<xmlReplayMaster>\r
<!-- Use this file to drive local testing. \r
- To run this file, do: \r
+\r
+ To run this file, copy the checked-in file to your own local version of the correct name\r
+ in the same location. If it is named dev-master.xml, then XmlReplayDevTest will pick it up.\r
+ If it is not there, XmlReplayDevTest will skip it, which is what we want for production, since \r
+ production runs XmlReplayMasterTest which calls xml-replay-master.xml\r
+ \r
+ cd trunk/services/IntegrationTests/src/test/resources/test-data/xmlreplay/\r
+ cp dev-master-example.xml dev-master.xml\r
+\r
+ IMPORTANT :: DO NOT CHECK IN YOUR LOCAL COPY, CALLED dev-master.xml\r
+ EITHER LEAVE IT AROUND AND IGNORE THE SVN UNVERSIONED FILE MESSAGE, \r
+ OR DELETE IT IF IT BOTHERS YOU.\r
+ \r
+ Then, to run this file, you can execute the following: \r
+ \r
cd trunk/services/IntegrationTests\r
mvn test -Dtest=XmlReplayDevTest\r
+\r
Maven surefire will kick off XmlReplayDevTest.\r
\r
- IMPORTANT :: DO NOT CHECK THIS FILE IN WITH ANY TESTS ACTIVE BELOW.\r
- Make sure all tests are commented out, or better yet, moved to dev-all.xml .\r
- This file should be modified locally and used instead of xml-replay-master.xml\r
- when you are developing. This file can be used to point at a controlFile \r
+ \r
+ This file can be used to point at a controlFile \r
and a testGroup, so you can test just one thing at a time, or a limited suite.\r
\r
You can find other tests to run in two places:\r
The set of possible tests, including ones that don't work:\r
dev-all.xml\r
-->\r
- <protoHostPort>http://localhost:8280</protoHostPort>\r
+ <protoHostPort>http://localhost:8180</protoHostPort>\r
\r
<!-- legal values for dumpServiceResult=[minimal,detailed,full,auto] -->\r
<dump payloads="false" dumpServiceResult="detailed" />\r
<auth ID="admin@collectionspace.org">YWRtaW5AY29sbGVjdGlvbnNwYWNlLm9yZzpBZG1pbmlzdHJhdG9y</auth>\r
</auths>\r
\r
- <!--\r
+ <!-- EXAMPLES. JUST UNCOMMENT THEM, AND RUN AS SHOWN ABOVE.\r
<run controlFile="person/person.xml" testGroup="PersonAddRelsDeleteRels" />\r
<run controlFile="location/location-hierarchy.xml" testGroup="HierarchicLocation" />\r
- -->\r
+ -->\r
</xmlReplayMaster>\r
\r
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>\r
-<xmlReplay>\r
- <!--\r
- How to use this file.\r
- ======================\r
- From a command-line, run, for example:\r
- cd C:\cs\trunk\services\common-test\r
- mvn exec:java\r
- To run one testGroup with ID="2collectionobjects", use:\r
- mvn -DtestGroupID=2collectionobjects exec:java\r
-\r
- To run one test with ID="1001" in one testGroup "2collectionobjects", use:\r
- mvn -DtestGroupID=2collectionobjects -DtestID=1001 exec:java\r
-\r
- If you need to compile, you can use:\r
- mvn -q install exec:java\r
-\r
- To make maven a bit more quiet (all the lovely INFO lines) run with\r
- mvn -q\r
- This class is not a test class, so if you want to skip other test classes, use\r
- mvn -DskipTests\r
- Example:\r
- mvn -q -DskipTests exec:java\r
-\r
- Maven knows where to find the command line args. It looks in the pom:\r
- C:\cs\trunk\services\common-test\pom.xml\r
- for the values under the element:\r
- /project/build/plugins/plugin/artifactId[exec-maven-plugin]\r
- However, you may override this with a full path\r
- mvn -DxmlReplayBaseDir="C:\tmp\xmlReplay" exec:java\r
- \r
- It always looks for this master config file, named "xml-replay-config.xml"\r
-\r
- For URLs, any ampersands must be escaped in this file in query strings like so: &\r
-\r
- The attribute\r
- /testGroup/test/@ID\r
- is optional. If present, it may be referred to in a DELETE using\r
- /testGroup/test/fromTestID/\r
- If it is not present, the driver will use\r
- /testGroup/test/part/filename\r
- as the ID for POST tests.\r
- If the attribute\r
- /testGroup/@autoDeletePOSTS\r
- is "true" then any POSTs that are sent that return a "Location:" header\r
- will be deleted with DELETE at the end of the test group.\r
- Alternately, you may delete them\r
- manually by providing a DELETE test as specified above.\r
-\r
- PUT methods also use the\r
- /testGroup/test/fromTestID/\r
- value to update the object posted by a POST test.\r
-\r
- -->\r
-\r
-<!--\r
- <testGroup ID="2collectionobjects" autoDeletePOSTS="true">\r
- <test ID="1001">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_whitesp1.xml</filename>\r
- </part>\r
- </test>\r
-\r
- <test ID="1002">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_whitesp2.xml</filename>\r
- </part>\r
- </test>\r
- </testGroup>\r
-\r
- <testGroup ID="2parts" autoDeletePOSTS="true">\r
- <test ID="nh">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <parts>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/nh-collectionobject.xml</filename>\r
- </part>\r
- <part>\r
- <label>collectionobjects_naturalhistory</label>\r
- <filename>collectionobject/nh-part.xml</filename>\r
- </part>\r
- </parts>\r
- </test>\r
- <test>\r
- <method>GET</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <fromTestID>nh</fromTestID>\r
- </test>\r
- </testGroup>\r
-\r
- <testGroup ID="collectionobjects" autoDeletePOSTS="true">\r
- <test ID="1001">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_whitesp1.xml</filename>\r
- </part>\r
- </test>\r
- <test ID="1002">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_whitesp2.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_whitesp3.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>DELETE</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <fromTestID>1001</fromTestID>\r
- </test>\r
- <test>\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_whitesp4.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/cspace-2242-first-value-instance-blank.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/cspace-2242-first-value-instance-nonblank.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/repfield_null1.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>GET</method>\r
- <uri>/cspace-services/collectionobjects/?sortBy=&pgNum=0&pgSz=8</uri>\r
- </test>\r
- <test ID="cambridge-1">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/testCambridge.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>GET</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <fromTestID>cambridge-1</fromTestID>\r
- </test>\r
- <test ID="cambridge-1-put">\r
- <method>PUT</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/testCambridge-update.xml</filename>\r
- </part>\r
- <fromTestID>cambridge-1</fromTestID>\r
- </test>\r
- </testGroup>\r
-\r
- <!-- You can run this with\r
- mvn -q -DtestGroupID=cambridge-put-demo exec:java\r
- -->\r
- <testGroup ID="cambridge-put-demo" autoDeletePOSTS="true">\r
- <test ID="cambridge-2">\r
- <method>POST</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/testCambridge.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>GET</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <fromTestID>cambridge-2</fromTestID>\r
- </test>\r
- <test ID="cambridge-2-put">\r
- <method>PUT</method>\r
- <uri>/cspace-services/collectionobjects/</uri>\r
- <part>\r
- <label>collectionobjects_common</label>\r
- <filename>collectionobject/testCambridge-update.xml</filename>\r
- </part>\r
- <fromTestID>cambridge-2</fromTestID>\r
- </test>\r
- </testGroup>\r
-\r
- <!-- You can run this with\r
- mvn -q -DtestGroupID=authrefs exec:java\r
- -->\r
- <testGroup ID="authrefs" autoDeletePOSTS="true">\r
- <test ID="newPersonAuthority">\r
- <method>POST</method>\r
- <uri>/cspace-services/personauthorities/</uri>\r
- <part>\r
- <label>personauthorities_common</label>\r
- <filename>authrefs/newPersonAuthority.xml</filename>\r
- </part>\r
- </test>\r
- <test>\r
- <method>GET</method>\r
- <uri>/cspace-services/personauthorities/</uri>\r
- <fromTestID>newPersonAuthority</fromTestID>\r
- </test>\r
- <test ID="newPerson">\r
- <method>POST</method>\r
- <uri>/cspace-services/personauthorities/</uri>\r
- <part>\r
- <label>persons_common</label>\r
- <filename>authrefs/newPerson.xml</filename>\r
- </part>\r
- <fromTestID>newPersonAuthority</fromTestID>\r
- </test>\r
-\r
- <test ID="personAuthsList">\r
- <method>LIST</method>\r
- <uri>/cspace-services/personauthorities/</uri>\r
- <fromTestID>newPersonAuthority</fromTestID>\r
- </test>\r
- </testGroup>\r
-\r
-\r
-\r
--->\r
- \r
-\r
-\r
-</xmlReplay>\r
return new Long((new java.util.Date()).getTime());\r
}\r
\r
+ public static String nowLocale(){\r
+ java.util.Date date = new java.util.Date();\r
+ String result = java.text.DateFormat.getDateTimeInstance().format(date);\r
+ date = null;\r
+ return result;\r
+ }\r
+\r
/** Handles null strings as empty. */\r
public static boolean isEmpty(String str){\r
return !notEmpty(str);\r
}\r
}\r
\r
- public static String prettyPrint(Document document) {\r
- String prettyHTML;\r
- try {\r
- StringWriter swriter = new StringWriter();\r
- OutputFormat format = OutputFormat.createPrettyPrint();\r
- format.setNewlines(true);\r
- format.setTrimText(true);\r
- format.setIndent(false);\r
- format.setXHTML(true);\r
- format.setLineSeparator(System.getProperty("line.separator")) ;\r
- HTMLWriter writer = new HTMLWriter(swriter, format);\r
- writer.write(document);\r
- writer.flush();\r
- prettyHTML = swriter.toString();\r
- } catch (Exception e){\r
- prettyHTML = "<?xml?><error>"+e+"</error>";\r
- }\r
- return prettyHTML;\r
- }\r
\r
/** This method takes a filename of a local file only; InputSource is not implemented yet.\r
*\r
--- /dev/null
+package org.collectionspace.services.common;
+
+import org.collectionspace.services.common.api.Tools;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.io.HTMLWriter;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+
+import java.io.StringWriter;
+
+public class XmlTools {
+
+
+ // @TODO Refactoring opportunity: the utility methods below
+ // could potentially be moved into the 'common' module,
+ // and made static and public.
+ // -- DONE. Moved here. Laramie20110519
+
+ // Output format for XML pretty printing.
+ public final static OutputFormat PRETTY_PRINT_OUTPUT_FORMAT = defaultPrettyPrintOutputFormat();
+
+
+ /**
+ * Returns a default output format for pretty printing an XML document.
+ *
+ * Uses the default settings for indentation, whitespace, etc.
+ * of a pre-defined dom4j output format.
+ *
+ * @return A default output format for pretty printing an XML document.
+ */
+ protected static OutputFormat defaultPrettyPrintOutputFormat() {
+
+ // Use the default pretty print output format in dom4j.
+ OutputFormat outformat = OutputFormat.createPrettyPrint();
+ // Supress the extra newline added after the XML declaration
+ // in that output format.
+ outformat.setNewLineAfterDeclaration(false);
+ return outformat;
+ }
+
+ /** Returns a pretty printed String representation of an XML document.
+ * @param doc A dom4j XML Document.
+ * @return A pretty printed String representation of an XML document.
+ */
+ public static String prettyPrintXML(Document doc) {
+
+ String xmlStr = "";
+ try {
+ xmlStr = formatXML(doc, PRETTY_PRINT_OUTPUT_FORMAT);
+ // If an error occurs during pretty printing, fall back to
+ // returning a default String representation of the XML document.
+ } catch (Exception e) {
+ System.err.println("Error pretty-printing XML: " + e.getMessage());
+ xmlStr = doc.asXML();
+ }
+
+ return xmlStr;
+ }
+
+ /**
+ * Returns a String representation of an XML document,
+ * formatted according to a specified output format.
+ * @param doc A dom4j XML Document.
+ * @param outformat A dom4j output format.
+ * @return A String representation of an XML document,
+ * formatted according to the specified output format.
+ * @throws Exception if an error occurs in printing
+ * the XML document to a String.
+ */
+ public static String formatXML(Document doc, OutputFormat outformat)
+ throws Exception {
+
+ StringWriter sw = new StringWriter();
+ try {
+ final XMLWriter writer = new XMLWriter(sw, outformat);
+ // Print the document to the current writer.
+ writer.write(doc);
+ }
+ catch (Exception e) {
+ throw e;
+ }
+ return sw.toString();
+ }
+
+ /**
+ * Returns an XML document, when provided with a String
+ * representation of that XML document.
+ * @param xmlStr A String representation of an XML document.
+ * @return A dom4j XML document.
+ */
+ public static Document textToXMLDocument(String xmlStr) throws Exception {
+
+ Document doc = null;
+ try {
+ doc = DocumentHelper.parseText(xmlStr);
+ } catch (DocumentException e) {
+ throw e;
+ }
+ return doc;
+ }
+
+ public static String prettyPrint(Document document) {
+ return prettyPrint(document, null);
+ }
+
+ public static String prettyPrint(Document document, String indentString) {
+ String prettyHTML;
+ try {
+ StringWriter swriter = new StringWriter();
+ OutputFormat format = OutputFormat.createPrettyPrint();
+ format.setNewlines(true);
+ format.setTrimText(true);
+ boolean indent = Tools.notEmpty(indentString);
+ format.setIndent(indent);
+ if (indent){
+ format.setIndent(indentString);
+ }
+ format.setXHTML(true);
+ format.setLineSeparator(System.getProperty("line.separator")) ;
+ HTMLWriter writer = new HTMLWriter(swriter, format);
+ writer.write(document);
+ writer.flush();
+ prettyHTML = swriter.toString();
+ } catch (Exception e){
+ prettyHTML = "<?xml?><error>"+e+"</error>";
+ }
+ return prettyHTML;
+ }
+}
// May at some point instead use
// org.jboss.resteasy.spi.NotFoundException
+import org.collectionspace.services.common.XmlTools;
import org.collectionspace.services.common.document.BadRequestException;
import org.collectionspace.services.common.document.DocumentNotFoundException;
final static String ID_GENERATOR_LIST_NAME = "idgenerator-list";
final static String ID_GENERATOR_LIST_ITEM_NAME = "idgenerator-list-item";
- // Output format for XML pretty printing.
- final static OutputFormat PRETTY_PRINT_OUTPUT_FORMAT =
- defaultPrettyPrintOutputFormat();
// Base URL path for REST-based requests to the ID Service.
//
/**
* Creates a new ID generator instance.
*
- * @param generatorRepresentation
- * A representation of an ID generator instance.
*/
@POST
@Path("")
// Append detailed information for this ID generator instance.
root = appendDetailedIDGeneratorInformation(root, instance);
- resourceRepresentation = prettyPrintXML(doc);
+ resourceRepresentation = XmlTools.prettyPrintXML(doc);
response =
Response.status(Response.Status.OK)
.entity(resourceRepresentation)
listitem = appendSummaryIDGeneratorInformation(listitem, csid);
}
- return prettyPrintXML(doc);
+ return XmlTools.prettyPrintXML(doc);
}
//////////////////////////////////////////////////////////////////////
generators.get(csid));
}
- return prettyPrintXML(doc);
+ return XmlTools.prettyPrintXML(doc);
}
//////////////////////////////////////////////////////////////////////
// components to a new XML document, copy its root element, and
// append it to the relevant location within the current element.
try {
- Document generatorDoc = textToXMLDocument(generatorStr);
+ Document generatorDoc = XmlTools.textToXMLDocument(generatorStr);
Element generatorRoot = generatorDoc.getRootElement();
generator.add(generatorRoot.createCopy());
// If an error occurs parsing the XML string representation,
}
- // @TODO Refactoring opportunity: the utility methods below
- // could potentially be moved into the 'common' module,
- // and made static and public.
-
- //////////////////////////////////////////////////////////////////////
- /**
- * Returns a default output format for pretty printing an XML document.
- *
- * Uses the default settings for indentation, whitespace, etc.
- * of a pre-defined dom4j output format.
- *
- * @return A default output format for pretty printing an XML document.
- */
- private static OutputFormat defaultPrettyPrintOutputFormat() {
-
- // Use the default pretty print output format in dom4j.
- OutputFormat outformat = OutputFormat.createPrettyPrint();
- // Supress the extra newline added after the XML declaration
- // in that output format.
- outformat.setNewLineAfterDeclaration(false);
- return outformat;
- }
-
- //////////////////////////////////////////////////////////////////////
- /**
- * Returns a pretty printed String representation of an XML document.
- *
- * @param doc A dom4j XML Document.
- *
- * @return A pretty printed String representation of an XML document.
- */
- private String prettyPrintXML(Document doc) {
-
- String xmlStr = "";
- try {
- xmlStr = formatXML(doc, PRETTY_PRINT_OUTPUT_FORMAT);
- // If an error occurs during pretty printing, fall back to
- // returning a default String representation of the XML document.
- } catch (Exception e) {
- if (logger.isDebugEnabled()) {
- logger.debug("Error pretty-printing XML: " + e.getMessage());
- }
- xmlStr = doc.asXML();
- }
-
- return xmlStr;
- }
-
- //////////////////////////////////////////////////////////////////////
- /**
- * Returns a String representation of an XML document,
- * formatted according to a specified output format.
- *
- * @param doc A dom4j XML Document.
- *
- * @param outformat A dom4j output format.
- *
- * @return A String representation of an XML document,
- * formatted according to the specified output format.
- *
- * @throws An Exception if an error occurs in printing
- * the XML document to a String.
- */
- private String formatXML(Document doc, OutputFormat outformat)
- throws Exception {
-
- StringWriter sw = new StringWriter();
- try {
- final XMLWriter writer = new XMLWriter(sw, outformat);
- // Print the document to the current writer.
- writer.write(doc);
- }
- catch (Exception e) {
- throw e;
- }
- return sw.toString();
- }
-
- //////////////////////////////////////////////////////////////////////
- /**
- * Returns an XML document, when provided with a String
- * representation of that XML document.
- *
- * @param xmlStr A String representation of an XML document.
- *
- * @return A dom4j XML document.
- */
- private Document textToXMLDocument(String xmlStr) throws Exception {
-
- Document doc = null;
- try {
- doc = DocumentHelper.parseText(xmlStr);
- } catch (DocumentException e) {
- throw e;
- }
- return doc;
- }
-
//////////////////////////////////////////////////////////////////////
/**
* Returns a relative URI path to a resource
\r
import org.collectionspace.services.common.IFragmentHandler;\r
import org.collectionspace.services.common.XmlSaxFragmenter;\r
+import org.collectionspace.services.common.XmlTools;\r
import org.collectionspace.services.common.api.FileTools;\r
import org.collectionspace.services.common.api.Tools;\r
import org.dom4j.Document;\r
}\r
}\r
public void onEndDocument(Document document, int fragmentCount){\r
- System.out.println("====TemplateExpander DONE============\r\n"+ XmlSaxFragmenter.prettyPrint(document)+"================");\r
+ System.out.println("====TemplateExpander DONE============\r\n"+ XmlTools.prettyPrint(document)+"================");\r
}\r
//============helper methods==============================================================\r
public FragmentHandlerImpl(String templateDir, String outputDir){\r
}\r
private void dump(Document context, String currentPath, int fragmentIndex, String fragment){\r
System.out.println("====Path============\r\n"+currentPath+'['+fragmentIndex+']');\r
- System.out.println("====Context=========\r\n"+ XmlSaxFragmenter.prettyPrint(context));\r
+ System.out.println("====Context=========\r\n"+ XmlTools.prettyPrint(context));\r
System.out.println("====Fragment========\r\n"+fragment+"\r\n===================\r\n");\r
}\r
}\r