001: package org.depunit;
002:
003: import org.jargp.*;
004: import java.lang.reflect.*;
005: import java.lang.annotation.*;
006: import org.depunit.annotations.*;
007: import java.util.*;
008: import java.util.concurrent.ConcurrentLinkedQueue;
009:
010: import java.io.*;
011: import org.w3c.dom.*;
012: import javax.xml.parsers.*;
013: import javax.xml.transform.*;
014: import javax.xml.transform.stream.*;
015: import javax.xml.transform.dom.*;
016: import static java.lang.System.out;
017:
018: public class DepUnit {
019: private static final String REGRESSION_FILE = ".depunit_regression";
020:
021: private static final ParameterDef[] PARAMETERS = {
022: new BoolDef('?', "help"), new BoolDef('e', "regression"),
023: new StringListDef('c', "classList"),
024: new StringDef('r', "reportFile"),
025: new StringDef('s', "styleSheet"),
026: new StringListDef('t', "tagList"),
027: new StringListDef('x', "xmlList"),
028: new NoFlagArgDef("targetMethods"),
029: new BoolDef('d', "debug") };
030:
031: private static class CommandLine {
032: public boolean help;
033: public boolean debug;
034: public boolean regression;
035: public List<String> classList;
036: public List<String> targetMethods;
037: public String reportFile;
038: public String styleSheet;
039: public List<String> xmlList;
040: public ArrayList<String> tagList;
041:
042: public CommandLine() {
043: help = false;
044: regression = false;
045: classList = new ArrayList<String>();
046: targetMethods = new ArrayList<String>();
047: reportFile = null;
048: styleSheet = null;
049: xmlList = new ArrayList<String>();
050: debug = false;
051: tagList = new ArrayList<String>();
052: }
053: }
054:
055: //---------------------------------------------------------------------------
056: private static void writeResults(Document doc, String reportFile,
057: String styleSheet) {
058: try {
059: TransformerFactory tf = TransformerFactory.newInstance();
060: Transformer trans;
061: if (styleSheet != null)
062: trans = tf.newTransformer(new StreamSource(new File(
063: styleSheet)));
064: else
065: trans = tf.newTransformer();
066:
067: trans.setOutputProperty("indent", "yes");
068: trans.transform(new DOMSource(doc), new StreamResult(
069: new File(reportFile)));
070: } catch (Exception e) {
071: e.printStackTrace();
072: }
073: }
074:
075: //---------------------------------------------------------------------------
076: private static Document createResultDocument() {
077: Document doc = null;
078:
079: try {
080: DocumentBuilder db = DocumentBuilderFactory.newInstance()
081: .newDocumentBuilder();
082: DOMImplementation di = db.getDOMImplementation();
083: doc = di.createDocument(null, "test_results", null);
084: } catch (Exception e) {
085: e.printStackTrace();
086: }
087:
088: return (doc);
089: }
090:
091: //---------------------------------------------------------------------------
092: private static void printHelp() {
093: out.println("DepUnit version X");
094: out
095: .println("Usage: java -jar depunit.jar [-e][-v] [-r <report file>] [-s <stylesheet>]");
096: out
097: .println(" ([-x <xml file> [-x ...] -t <tag> [-t ...]]|([-c <test class> [-c ...]]");
098: out.println(" [<target method> ...]))");
099: out.println(" -e: Runs DepUnit in regression mode.");
100: out.println(" -r: Name of the xml report file to generate.");
101: out.println(" -s: Stylesheet to use to style the report.");
102: out
103: .println(" -x: XML input file that defines a suite of test runs.");
104: out.println(" -c: Test class to include in the run.");
105: out
106: .println(" -t: Only test runs marked with this tag will run.");
107: out.println(" target methods: Specific test methods to run.");
108: }
109:
110: private static CommandLine cl;
111:
112: //---------------------------------------------------------------------------
113: public static void main(String[] args) throws Exception {
114: int failCount = 0;
115: cl = new CommandLine();
116: ArgumentProcessor proc = new ArgumentProcessor(PARAMETERS);
117: proc.processArgs(args, cl);
118:
119: if (cl.help || (args.length == 0)) {
120: printHelp();
121: return;
122: }
123:
124: Document doc = createResultDocument();
125: if (cl.xmlList.size() > 0) {
126: for (String xmlFile : cl.xmlList) {
127: failCount += runSuite(xmlFile, doc, cl.tagList);
128: //Have a bailout switch when fail occurs
129: }
130: } else {
131: DepUnit du = new DepUnit(new TestRun(cl.classList,
132: cl.targetMethods), cl.debug);
133:
134: //Could call this from a Thread class using multiple threads
135: du.run();
136:
137: du.writeReport("", doc);
138:
139: failCount = du.getFailedCount();
140: }
141:
142: if (cl.reportFile != null) {
143: writeResults(doc, cl.reportFile, cl.styleSheet);
144: }
145:
146: if (cl.regression) {
147: writeResults(doc, REGRESSION_FILE, null);
148: }
149:
150: if (failCount == 0) {
151: File regressionFile = new File(REGRESSION_FILE);
152: if (regressionFile.exists())
153: regressionFile.delete();
154: }
155:
156: System.exit(failCount);
157: }
158:
159: //---------------------------------------------------------------------------
160: private static int runSuite(String xmlFile, Document results,
161: ArrayList<String> tagList) {
162: int failCount = 0;
163: try {
164: int verbosity = 0;
165: DocumentBuilderFactory dbf = DocumentBuilderFactory
166: .newInstance();
167: dbf.setValidating(false);
168: DocumentBuilder db = dbf.newDocumentBuilder();
169: Document xmldoc = db.parse(new File(xmlFile));
170:
171: Element root = xmldoc.getDocumentElement();
172: String verbose = root.getAttribute("verbose");
173: if ((verbose != null) && (!verbose.equals("")))
174: verbosity = Integer.parseInt(verbose);
175:
176: //Read in the class groups
177: NodeList nl = xmldoc.getElementsByTagName("classGroup");
178:
179: Map<String, List<TestRun.ClassConfig>> classGroups = new HashMap<String, List<TestRun.ClassConfig>>();
180:
181: for (int I = 0; I < nl.getLength(); I++) {
182: Element group = (Element) nl.item(I);
183: String groupName = group.getAttribute("name");
184:
185: NodeList cnl = group.getElementsByTagName("class");
186: List<TestRun.ClassConfig> classList = new ArrayList<TestRun.ClassConfig>();
187:
188: for (int J = 0; J < cnl.getLength(); J++)
189: classList.add(new TestRun.ClassConfig((Element) cnl
190: .item(J)));
191:
192: classGroups.put(groupName, classList);
193: }
194:
195: //Read in each run
196: nl = xmldoc.getElementsByTagName("run");
197: for (int I = 0; I < nl.getLength(); I++) {
198: Element run = (Element) nl.item(I);
199: String runName = run.getAttribute("name");
200:
201: //Look for tags
202: if (tagList.size() > 0) {
203: boolean proceed = false;
204: NodeList tagNList = run.getElementsByTagName("tag");
205: for (int i = 0; i < tagNList.getLength(); i++) {
206: String tag = tagNList.item(i).getFirstChild()
207: .getNodeValue();
208: if (tagList.contains(tag)) {
209: //We will only run the "run" if it has a tag that was specified
210: proceed = true;
211: break;
212: }
213: }
214:
215: if (!proceed)
216: continue;
217: }
218:
219: DepUnit du = new DepUnit(new TestRun(run, classGroups),
220: cl.debug);
221: du.setVerbosity(verbosity);
222:
223: if (verbosity > 0)
224: System.out.println("Test Run: " + runName);
225: du.run();
226: du.writeReport(runName, results);
227: failCount += du.getFailedCount();
228: }
229: } catch (InitializationException ie) {
230: if (ie.getCause() != null) {
231: System.out.println(ie.getCause());
232: ie.getCause().printStackTrace();
233: } else
234: System.out.println(ie);
235: } catch (XMLException xmle) {
236: System.out.println(xmle);
237: } catch (Exception e) {
238: System.out.println(e);
239: e.printStackTrace();
240: }
241:
242: return (failCount);
243: }
244:
245: //===========================================================================
246: //private HashSet<TestMethod> m_queueLookup;
247: //private Queue<TestMethod> m_processQueue;
248: //private List<TestMethod> m_reportList; //Contains the same as m_processQueue, used for reporting
249: private ProcessQueue m_processQueue;
250: private Queue<TestResult> m_resultList;
251: private HashMap<String, TestMethod> m_tmBucket;
252: private HashMap<String, TestMethod> m_targetBucket;
253: private HashMap<String, List<TestMethod>> m_groupBucket;
254: private List<TestMethod> m_testMethods;
255: private Stack<TestMethod> m_cleanupStack;
256: private Map<String, Object> m_runParams;
257: private int m_verbosity;
258: private boolean m_debug;
259:
260: public DepUnit(TestRun testRun, boolean debug)
261: throws ClassNotFoundException, MissingDependencyException {
262: setDebug(debug);
263: //System.out.println("New DepUnit");
264: m_verbosity = 0;
265: //m_queueLookup = new HashSet<TestMethod>();
266: //m_processQueue = new ConcurrentLinkedQueue<TestMethod>();
267: //m_reportList = new ArrayList<TestMethod>();
268: m_processQueue = new ProcessQueue();
269: m_resultList = new ConcurrentLinkedQueue<TestResult>();
270: m_tmBucket = new HashMap<String, TestMethod>();
271: m_targetBucket = new HashMap<String, TestMethod>();
272: m_testMethods = new LinkedList<TestMethod>();
273: m_cleanupStack = new Stack<TestMethod>();
274: m_groupBucket = new HashMap<String, List<TestMethod>>();
275: m_runParams = new HashMap<String, Object>();
276:
277: Collection<TestRun.ClassConfig> classes = testRun.getClasses();
278: for (TestRun.ClassConfig cc : classes) {
279: if (cc.getDataDriver() != null)
280: addClass(cc.getName(), cc.getDataDriver());
281: else
282: addClass(cc.getName(), cc.getParams());
283: }
284:
285: List<TestMethod> targetList;
286: List<TestRun.MethodConfig> methods = testRun.getMethods();
287: if (methods.size() == 0)
288: targetList = getTestMethods("*");
289: else {
290: targetList = new ArrayList<TestMethod>();
291: for (TestRun.MethodConfig m : methods) {
292: targetList.addAll(getTestMethods(m.getName()));
293: //Somewhere in here you need to set parameters from the config
294: //to the TestMethod class
295: }
296: }
297:
298: createProcessQueue(targetList);
299: }
300:
301: //===========================================================================
302: public void setDebug(boolean debug) {
303: out.println("Debug = " + debug);
304: m_debug = debug;
305: }
306:
307: //---------------------------------------------------------------------------
308: public List<TestMethod> getTestMethods(String name) {
309: List<TestMethod> retList;
310:
311: if (name.equals("*"))
312: retList = new ArrayList<TestMethod>(m_testMethods);
313: else if ((retList = m_groupBucket.get(name)) != null)
314: retList = new ArrayList<TestMethod>(retList);
315: else {
316: retList = new ArrayList<TestMethod>();
317: retList.add(m_tmBucket.get(name));
318: }
319:
320: return (retList);
321: }
322:
323: //---------------------------------------------------------------------------
324: public void setVerbosity(int level) {
325: m_verbosity = level;
326: }
327:
328: //---------------------------------------------------------------------------
329: public void createProcessQueue(List<TestMethod> targetMethods)
330: throws MissingDependencyException {
331: Set<String> cleanupSet = new HashSet<String>();
332:
333: if (m_debug) {
334: out.println("Target methods:");
335: Iterator<TestMethod> it = targetMethods.iterator();
336: while (it.hasNext()) {
337: TestMethod tm = it.next();
338: if (tm != null)
339: out.println(" " + tm.getFullName());
340: }
341: }
342:
343: //Add methods to target bucket, used for soft dependency lookup
344: for (TestMethod tm : targetMethods)
345: m_targetBucket.put(tm.getFullName(), tm);
346:
347: /*
348: The next four steps need to be done seperately so recursive dependencies
349: are not introduced
350: */
351:
352: //Resolving the cleanup methods will add dependencies that must be set before proceeding
353: for (TestMethod tm : m_testMethods)
354: //Resolve all methods
355: tm.resolveCleanupMethods(m_tmBucket);
356:
357: //need to do this first before adding to process queue
358: for (TestMethod tm : m_testMethods)
359: //Resolve all methods
360: tm.resolveDependencies(m_groupBucket, m_tmBucket);
361:
362: //Prep the cleanup methods
363: for (TestMethod tm : m_testMethods)
364: tm.gatherCleanupDependencies(m_tmBucket);
365:
366: //If new deps were added in the last set then this will set Observers
367: for (TestMethod tm : m_testMethods)
368: tm.addCleanupDependencies(m_tmBucket);
369:
370: //Add only specified target methods if they are not cleanup methods
371: for (TestMethod tm : targetMethods) {
372: addToProcessQueue(tm);
373: }
374:
375: //Adds cleanup methods to the end of the run
376: while (!m_cleanupStack.empty())
377: addToProcessQueue(m_cleanupStack.pop());
378:
379: //Now mark all other methods as not_run
380: for (TestMethod tm : m_testMethods) {
381: if (!m_processQueue.contains(tm))
382: tm.setStatus(TestMethod.STATUS_NOT_RAN);
383: }
384: }
385:
386: //---------------------------------------------------------------------------
387: public void run() {
388: TestMethod tm;
389: while ((tm = m_processQueue.getNextMethod()) != null) {
390: TestResult tr = new TestResult(tm);
391: m_resultList.add(tr);
392: TestClass tc = tm.getTestClass();
393:
394: //System.out.println("Blocking");
395: tm.blockNRun(); //Wait until dependencies are satisfied
396: //System.out.println("done Blocking");
397:
398: try {
399: if (tm.getStatus() != null) {
400: tm.skipMethod(m_runParams);
401: tr.setStatus(tm.getStatus());
402: System.out
403: .println(" Skipping " + tm.getFullName());
404: continue;
405: }
406:
407: if (!tm.isProcessMethod())
408: tc.callBeforeTest();
409:
410: //Call tm method
411:
412: if (m_verbosity > 0)
413: System.out.println(" " + tm.getFullName());
414: tm.callMethod(m_runParams);
415: tr.setStatus(TestResult.STATUS_SUCCESS);
416: tm.setStatus(TestMethod.STATUS_SUCCESS);
417: } catch (ObjectCreationException oce) {
418: if (m_debug) {
419: System.out.println("OBJECT CREATION EXCEPTION");
420: oce.printStackTrace(System.out);
421: }
422: //Could not instanciate object
423: } catch (IllegalAccessException iae) {
424: if (m_debug) {
425: System.out.println("ILLEGAL ACCESS EXCEPTION");
426: iae.printStackTrace(System.out);
427: }
428: } catch (InitializationException ie) {
429: if (m_debug)
430: System.out.println("INITIALIZATION EXCEPTION");
431:
432: ie.printStackTrace(System.out);
433: tm.setStatus(TestResult.STATUS_FAILED);
434: tr.setStatus(TestResult.STATUS_FAILED);
435: tr.setException(ie);
436: } catch (InvocationTargetException ite) {
437: if (m_debug)
438: System.out.println("INVOCATION TARGET EXCEPTION");
439:
440: Throwable t = ite.getCause();
441: tm.setStatus(TestResult.STATUS_FAILED);
442: tr.setStatus(TestResult.STATUS_FAILED);
443: tr.setException(t);
444: System.out.println(tm.printStack(t));
445: }
446:
447: if (!tm.isProcessMethod())
448: tc.callAfterTest();
449: }
450: }
451:
452: //---------------------------------------------------------------------------
453: private void addToGroupBucket(TestMethod tm) {
454: String[] groups = tm.getGroups();
455: if (groups == null)
456: return;
457:
458: for (String group : groups) {
459: List groupList = m_groupBucket.get(group);
460: if (groupList == null) {
461: groupList = new ArrayList<TestMethod>();
462: m_groupBucket.put(group, groupList);
463: }
464:
465: groupList.add(tm);
466: }
467: }
468:
469: //---------------------------------------------------------------------------
470: private void addClass(String className, DataDriver dd)
471: throws ClassNotFoundException {
472: TestClass tc = new TestClass(className);
473: tc.setDataDriver(dd);
474: List<TestMethod> methods = tc.getTestMethods();
475:
476: for (TestMethod tm : methods) {
477: m_tmBucket.put(tm.getFullName(), tm);
478: m_testMethods.add(tm);
479: addToGroupBucket(tm);
480: }
481: }
482:
483: //---------------------------------------------------------------------------
484: public void addClass(String className,
485: Map<String, String> initParams)
486: throws ClassNotFoundException {
487: if (initParams != null)
488: addClass(className,
489: new TestClass.InitDataDriver(initParams));
490: else
491: addClass(className, (DataDriver) null);
492: }
493:
494: //---------------------------------------------------------------------------
495: public void addToProcessQueue(TestMethod method)
496: throws MissingDependencyException {
497: //To make this faster put added methods in a HashSet and check against that
498: if (m_processQueue.contains(method))
499: return;
500:
501: if (m_debug)
502: out.println("Adding method " + method.getFullName());
503:
504: //Add hard dependencies
505: List<String> hardDep = method.getHardDependencies();
506: for (String dep : hardDep) {
507: TestMethod m = m_tmBucket.get(dep);
508: if (m == null)
509: throw new MissingDependencyException(dep);
510: addToProcessQueue(m);
511: }
512:
513: //Add soft dependencies
514: List<String> softDep = method.getSoftDependencies();
515: for (String dep : softDep) {
516: TestMethod tm = m_targetBucket.get(dep);
517: if (tm != null)
518: addToProcessQueue(tm);
519: }
520:
521: //add to queue
522: m_processQueue.add(method);
523:
524: //Add cleanup methods to cleanup list
525: List<String> cleanupList = method.getCleanupMethods();
526: for (String cleanup : cleanupList)
527: m_cleanupStack.push(m_tmBucket.get(cleanup));
528: }
529:
530: //---------------------------------------------------------------------------
531: public int getFailedCount() {
532: int count = 0;
533:
534: for (TestResult tr : m_resultList) {
535: if (tr.getStatus().equals(TestResult.STATUS_FAILED))
536: count++;
537: }
538:
539: return (count);
540: }
541:
542: //---------------------------------------------------------------------------
543: //public void writeReport(String fileName, String styleSheet)
544: public void writeReport(String runName, Document doc) {
545: try {
546: Element root = doc.getDocumentElement();
547:
548: int total = m_testMethods.size();
549: int passed = 0;
550: int failed = 0;
551: int skipped = 0;
552: int not_ran = 0;
553:
554: for (TestResult tr : m_resultList) {
555: String status = tr.getStatus();
556: if (status.equals(TestMethod.STATUS_SUCCESS))
557: passed++;
558: else if (status.equals(TestMethod.STATUS_FAILED))
559: failed++;
560: else if (status.equals(TestMethod.STATUS_SKIPPED))
561: skipped++;
562: else
563: not_ran++;
564: }
565:
566: Element run = doc.createElement("run");
567: root.appendChild(run);
568: run.setAttribute("name", runName);
569: run.setAttribute("total", String.valueOf(total));
570: run.setAttribute("passed", String.valueOf(passed));
571: run.setAttribute("failed", String.valueOf(failed));
572: run.setAttribute("skipped", String.valueOf(skipped));
573: run.setAttribute("not_ran", String.valueOf(not_ran));
574:
575: for (TestResult tr : m_resultList) {
576: Element test = doc.createElement("test");
577:
578: tr.reportStatus(doc, test);
579:
580: run.appendChild(test);
581: }
582:
583: } catch (Exception e) {
584: e.printStackTrace();
585: }
586:
587: }
588: }
|