001: package U2.T2;
002:
003: import U2.T2.Obj.*;
004: import U2.T2.Msg.*;
005: import U2.T2.Reflection.*;
006: import java.util.*;
007: import java.io.*;
008: import java.util.logging.*;
009: import java.lang.reflect.*;
010: import jargs.gnu.CmdLineParser;
011:
012: /**
013: * Implements test engine for random testing; it provides the T2's
014: * <b>main testing API</b>.
015: *
016: * <p>An instance of this class is a test engine. When given a target
017: * class to test, an engine randomly generates executions, and also
018: * randomly generates objects needed as parameters for method called
019: * during the executions. The executions are checked against the class
020: * invariant of the target class and its other specifications if
021: * present.
022: *
023: *
024: * <h3>What is being checked?</h3>
025: *
026: * <p>An instance of this class is a test engine. An engine generates
027: * executions, looking for <u>violations</u>. Executions leading to
028: * violations are reported and can be saved for regression. These are
029: * the kinds of violations checked by the engine:
030:
031: * <ol>
032: *
033: * <p><li>INTERNAL error. When a method of the target object raises an
034: * Error, which is not an AssertionError, or RuntimeException.
035: *
036: * <p><li>ASSERTION violation. When a method (or its specification)
037: * raises an AssertionError, which is not marked by "PRE". If it is
038: * marked by PRE then its pre-condition is not met, in which case the
039: * execution will be discarded.
040: *
041: * <p><li>CLASSINV violation. This happens when the state after a step
042: * in an execution violates the class invariant.
043: *
044: * <p>Exception thrown by the class invariant will be considered as
045: * false. If it throws an unchecked exception, the engine will give a
046: * warning.
047: *
048: * </ol>
049: *
050: * @see U2.T2.Engine
051: */
052: public class RndEngine extends Engine {
053:
054: /**
055: * The base domain used by the engine.
056: */
057: protected BaseDomain baseDomain;
058:
059: /**
060: * A random generator.
061: */
062: protected Random rnd;
063:
064: /**
065: * In the current implementation the number of relevant checks is
066: * a bit difficult to count; so we count instead the number of
067: * irrelevant checks.
068: */
069: protected int numOfIrrelevantChecks = 0;
070:
071: /**
072: * Specify a maximum on the length of every generated mkVal
073: * sequence. Default is 4.
074: */
075: protected int maxMkValSeqDepth = 4;
076:
077: // More option parameters:
078:
079: /**
080: * The probability to do field update rather than method call.
081: * Default is 0.1.
082: */
083: protected float chooseFieldUpdateProb = (float) 0.1;
084:
085: /**
086: * When trying to generate an instance of C, and one cannot be
087: * found in the base domain, and the engine decided not to look in
088: * the pool (hence generating a fresh one), this is the
089: * probability of generating null. If less or equal to null, then
090: * generating null in this way will be surpressed.
091: */
092: protected float generateNullProb = (float) 0.1;
093:
094: /**
095: * The probability that when a new execution is to be generated,
096: * the target object will be picked from the pool rather than
097: * created fresh with a constructor. Default is 0.7.
098: */
099: protected float pickTargetObjFromPoolProb = (float) 0.7;
100:
101: /**
102: * The probability for chosing to pass the target object as a
103: * parameter rather than as a receiver object. Default is 0.4.
104: */
105: protected float passTObjAsParamProb = (float) 0.4;
106:
107: /**
108: * Maximum collection and array length (along each dimension). The
109: * default is 4.
110: */
111: protected int maxArrayLength = 5;
112:
113: /**
114: * Maximum depth when showing objects in reporting. That is,
115: * subobjects at deeper than this depth will not be shown. The
116: * default is 5.
117: */
118: protected int maxShowDepth = 5;
119:
120: /**
121: * Collected execution traces. Violating executions are collected
122: * here, but future versions may add random traces to be
123: * collected. When the engine is finished, collected traces are
124: * saved and can be reloaded and replayed by a regression engine.
125: */
126: protected LinkedList<Trace> collectedTraces;
127:
128: /**
129: * An interface map used by the engine. The map lists interfaces,
130: * and for each possible concrete classes (currently juts one)
131: * that implement it. When the engine needs to generate an
132: * instance of an interface I, it first look it up in this
133: * interface map. If the map says it has implementations, one
134: * (say, C) will be selected and the engine will then proceed by
135: * trying to generate an instance of C instead.
136: */
137: protected InterfaceMap interfaceMap;
138:
139: // Constructors, fields, and methods that are considered to take
140: // part as interaction points to CUT. For now they are simply
141: // CUT's non-private constructors and methods:
142:
143: private List<Constructor> consIPs;
144: private List<Field> fieldsIPs;
145: private List<Method> methodsIPs;
146: // methods that can accept CUT in their parameters:
147: private List<Method> methodsCanAcceptCUTinParam;
148: // methods that can accept CUT as their receiver:
149: private List<Method> methodsCanAcceptCUTinRec;
150:
151: // specifications found in CUT:
152: private Method classINV;
153: // class invariants of the supper-classes; currently not used. INCOMPLETE
154: private Method[] supperINV;
155: private HashMap<Method, Method> methodSpecs;
156:
157: // The type of the elements of collections. Whenever the engine
158: // has to create a collection, it needs to know the type of its
159: // elements; however due to Java's type erasure this info is not
160: // available via reflection. FOR NOW we'll pass this info via the
161: // top level option, and store it in this variable. This is NOT a
162: // generic solution. It only works for collection, and applies
163: // globally. That is, the engine simply assumes that all
164: // collections it has to generate will have this type of elements.
165: private Class TYVAR0 = null;
166:
167: // Logger:
168: private static Logger logger = Logger.getLogger("U2.T2");
169:
170: /**
171: * Check if a given class is of the right kind (e.g. it is not an interface)
172: * to be checked with this engine. It the class is not of the right,
173: * messages will also be logged.
174: */
175: public static boolean doesTargetClassConform(Class C) {
176:
177: String name = C.getSimpleName();
178:
179: if (C == null) {
180: logger.log(Level.WARNING, Message.mk(Message.PROBLEM,
181: "Null is passed as the target class."));
182: return false;
183: }
184: if (Modifier.isAbstract(C.getModifiers())) {
185: logger.log(Level.WARNING, Message.mk(Message.PROBLEM,
186: "Target class " + name + " is abstract."));
187: return false;
188: }
189: if (C.isInterface()) {
190: logger.log(Level.WARNING, Message.mk(Message.PROBLEM,
191: "Target class " + name + " is an interface."));
192: return false;
193: }
194: if (ReflUtil.getPubCons(C).size() <= 0) {
195: logger.log(Level.WARNING, Message.mk(Message.PROBLEM,
196: "Target class " + name
197: + " has no public constructor."));
198: return false;
199:
200: }
201: // ELSE:
202: return true;
203: }
204:
205: /**
206: * Create a test engine, with C as the target class, using the
207: * given pool, base domain, and interface map.
208: */
209: public RndEngine(Class C, Pool p, BaseDomain dom, InterfaceMap imap) {
210:
211: if (!doesTargetClassConform(C)) {
212:
213: Message.throwT2Error(T2Error.ABORT,
214: "An instance of RndEngine cannot be created.");
215: }
216:
217: rnd = new Random();
218: pool = p;
219: baseDomain = dom;
220: CUT = C;
221: interfaceMap = imap;
222:
223: try {
224: classINV = CUT.getMethod("classinv", (Class[]) null);
225: // check if it is of the right type, if not ignore :
226: if (classINV.getReturnType() != Boolean.TYPE
227: || !Modifier.isPublic(classINV.getModifiers()))
228: classINV = null;
229: } catch (Exception e) {
230: classINV = null;
231: }
232:
233: if (classINV == null)
234: logger.log(Level.WARNING, Message.mk(Message.PROBLEM, C
235: .getName()
236: + " does not specify a class invariant."));
237:
238: collectedTraces = new LinkedList<Trace>();
239:
240: consIPs = Engine.getConsIPs(C);
241: fieldsIPs = Engine.getFieldsIPs(C);
242: methodsIPs = Engine.getMethsIPs(C);
243: methodSpecs = Engine.mk_specMap(C, methodsIPs);
244:
245: // open access to aux_laststep hook, if provided by CUT:
246: try {
247: Field aux_laststep_field = C
248: .getDeclaredField("aux_laststep");
249: if (aux_laststep_field.getType().getName().equals(
250: "java.lang.String"))
251: aux_laststep_field.setAccessible(true);
252: } catch (Exception e) {
253: }
254:
255: methodsCanAcceptCUTinParam = new LinkedList<Method>();
256: methodsCanAcceptCUTinRec = new LinkedList<Method>();
257:
258: for (Method m : methodsIPs) {
259:
260: if (ReflUtil.canAcceptAsParameter(CUT, m))
261: methodsCanAcceptCUTinParam.add(m);
262: if (!Modifier.isStatic(m.getModifiers()))
263: methodsCanAcceptCUTinRec.add(m);
264: }
265: }
266:
267: /**
268: * A method to construct a mk_val step.
269: */
270: private Trace.MkValStep MK_VAL_STEP(Class C, int depth) {
271:
272: // System.out.print(".") ;
273:
274: // If C is in the base domain, always generate it, even if the
275: // depth exceeds max:
276:
277: Object[] r = baseDomain.rndGET(C);
278: if (r != null)
279: return new Trace.CONST(r[0], null);
280:
281: // If it is not base, and max depth is exceeded, then return
282: // null:
283: if (depth >= maxMkValSeqDepth)
284:
285: return (new Trace.CONST(null, C));
286:
287: // Consult the interface map first:
288: Class C_implementation = interfaceMap.getImplementation(C);
289: if (C_implementation != null)
290: return MK_VAL_STEP(C_implementation, depth);
291:
292: // If C is class "Object", replace it with TYVAR0 if defined,
293: // else with CUT:
294: if (C.getName().equals("java.lang.Object")) {
295: if (TYVAR0 != null)
296: return MK_VAL_STEP(TYVAR0, depth);
297: else
298: return MK_VAL_STEP(CUT, depth);
299: }
300:
301: // Else pick from the pool or generate fresh:
302:
303: if (rnd.nextFloat() <= pickTargetObjFromPoolProb) {
304:
305: // Deciding to pick the target object from the pool:
306: Integer index = pool.rndGetIndex(C);
307:
308: if (index != null)
309: return new Trace.REF(index);
310: }
311:
312: // ELSE we have to generate a fresh object
313:
314: // To generate null:
315: if (generateNullProb > 0 && rnd.nextFloat() <= generateNullProb)
316: return (new Trace.CONST(null, C));
317:
318: // If C is an array, linkedlist, or hashset, then make
319: // one. Currently the maximum size is 4.
320: Class ClassCollection = null;
321: try {
322: ClassCollection = Class.forName("java.util.Collection");
323: } catch (Exception e) {
324: }
325: if (C.isArray()
326: || (!C.isInterface() && ClassCollection
327: .isAssignableFrom(C))) {
328:
329: Class elemType = null;
330: if (C.isArray())
331: elemType = C.getComponentType();
332: else
333: // Deciding the element type of the collection; for
334: // now we'll defaul it to TYVAR0 if it is not
335: // null. TYVAR0 can be set via top level option. If it
336: // is null, we'll default to CUT:
337: if (TYVAR0 != null)
338: elemType = TYVAR0;
339: else
340: elemType = CUT;
341:
342: int size = rnd.nextInt(maxArrayLength);
343: Trace.MkValStep[] elems = new Trace.MkValStep[size];
344: for (int i = 0; i < size; i++)
345: elems[i] = MK_VAL_STEP(elemType, depth + 1);
346:
347: String collectionType = Trace.ARRAY;
348: if (!C.isArray())
349: collectionType = C.getName();
350: return new Trace.CREATE_COLLECTION_LIKE(collectionType,
351: elemType, size, elems);
352: }
353:
354: // else, creating a fresh object from a constructor.
355:
356: List<Constructor> constructors = null;
357: if (C == CUT)
358: constructors = consIPs;
359: else
360: constructors = ReflUtil.getPubCons(C);
361:
362: if (constructors.isEmpty()) // C has no constructor! Would just return null:
363:
364: return (new Trace.CONST(null, C));
365: // ELSE:
366: Constructor chosenConstructor = constructors.get(rnd
367: .nextInt(constructors.size()));
368: Class[] paramTypes = chosenConstructor.getParameterTypes();
369: Trace.MkValStep[] params = new Trace.MkValStep[paramTypes.length];
370:
371: for (int i = 0; i < paramTypes.length; i++)
372:
373: params[i] = MK_VAL_STEP(paramTypes[i], depth + 1);
374:
375: return
376:
377: (new Trace.CREATE_TARGET_OBJECT(chosenConstructor, params,
378: // note that it is
379: // important that the
380: // index is set to -1
381: // here:
382: -1));
383:
384: }
385:
386: /**
387: * Same as MK_VAL_STEP, but this should create a non-null
388: * object. Currently implemented by repeatedly trying MK_VAL_STEP
389: * until it obtains non-null. So it may not terminate.
390: */
391: private Trace.MkValStep MK_NONNULL_VAL_STEP(Class C, int depth) {
392:
393: Trace.MkValStep step = MK_VAL_STEP(C, depth);
394: while ((step instanceof Trace.CONST && ((Trace.CONST) step)
395: .isNull())
396: || (step instanceof Trace.REF && (pool
397: .get(((Trace.REF) step).getIndex()) == null)))
398:
399: step = MK_VAL_STEP(C, depth);
400:
401: return step;
402: }
403:
404: private static <T> T getRndElem(Random rnd, List<T> s) {
405: if (s.isEmpty())
406: return null;
407: else
408: return s.get(rnd.nextInt(s.size()));
409: }
410:
411: /**
412: * A method to construct a step of an execution trace.
413: */
414: private Trace.TraceStep TRACE_STEP(int indexOfTargetObj) {
415:
416: if (rnd.nextFloat() <= chooseFieldUpdateProb
417: && fieldsIPs.size() > 0) {
418:
419: Field chosen = getRndElem(rnd, fieldsIPs);
420:
421: return (new Trace.UPDATE_FIELD(chosen, MK_VAL_STEP(chosen
422: .getType(), 0)));
423: }
424:
425: // else the step is a method call:
426:
427: Method method = getRndElem(rnd, methodsCanAcceptCUTinRec);
428:
429: // if the method has a specification, replace it with its
430: // specification:
431: if (methodSpecs.containsKey(method))
432: method = methodSpecs.get(method);
433:
434: int whichParam = -1;
435:
436: Class[] paramTypes = method.getParameterTypes();
437:
438: if (rnd.nextFloat() <= passTObjAsParamProb
439: && methodsCanAcceptCUTinParam.size() > 0) {
440:
441: // Deciding to pass target object as parameter instead
442:
443: method = getRndElem(rnd, methodsCanAcceptCUTinParam);
444:
445: paramTypes = method.getParameterTypes();
446:
447: LinkedList<Integer> possiblePlaces = new LinkedList<Integer>();
448: for (int i = 0; i < paramTypes.length; i++)
449: if (paramTypes[i].isAssignableFrom(CUT))
450: possiblePlaces.add(i);
451: whichParam = getRndElem(rnd, possiblePlaces);
452:
453: }
454:
455: Trace.MkValStep[] params = new Trace.MkValStep[paramTypes.length];
456:
457: for (int i = 0; i < params.length; i++) {
458:
459: // note that i>=0, so if whichParam=-1 the guard is false
460: // anyway:
461: if (i == whichParam)
462: params[i] = new Trace.REF(indexOfTargetObj);
463:
464: else
465: params[i] = MK_VAL_STEP(paramTypes[i], 0);
466:
467: }
468:
469: if (Modifier.isStatic(method.getModifiers()))
470:
471: return (new Trace.CALL_METHOD(method, null, params));
472:
473: // else, if method is not static:
474:
475: Trace.MkValStep receiver = null;
476:
477: if (whichParam < 0)
478: receiver = new Trace.REF(indexOfTargetObj);
479:
480: else
481: receiver = MK_NONNULL_VAL_STEP(method.getDeclaringClass(),
482: 0);
483:
484: return (new Trace.CALL_METHOD(method, receiver, params));
485:
486: }
487:
488: /**
489: * The method to run the engine (that is, to run an automated
490: * random testing).
491: */
492: public void runMe() {
493:
494: Trace sigma = null;
495: Trace.TraceStep step = null;
496: int depth = 0; // the length of sigma
497: Trace.ExecResult stepResult = null;
498: boolean invOk = true;
499:
500: if (maxExecLength <= 0)
501: return;
502:
503: while (numOfSteps < maxNumOfSteps
504: && numOfViolations < maxNumViolations) {
505:
506: // numOfSteps is now actually number of iterations:
507: numOfSteps++;
508:
509: if (depth >= maxExecLength) { // max depth exceeded, reset sigma:
510: depth = 0;
511: sigma = null;
512: continue;
513: }
514: // ELSE:
515:
516: if (sigma == null) { // create the target object first:
517: // don't forget to reset the pool !!
518: pool.reset();
519: sigma = new Trace();
520: sigma.creation = MK_NONNULL_VAL_STEP(CUT, 0);
521: try {
522: stepResult = Trace.MkTargetObject(pool,
523: sigma.creation, classINV);
524: sigma.indexOfTargetObj = ((Trace.CREATE_TARGET_OBJECT) sigma.creation).indexOfNewObject;
525:
526: } catch (InvocationTargetException e) {
527: // creation fails, discard:
528: depth = 0;
529: sigma = null;
530: continue;
531: }
532: // so the creation was successful:
533: numOfExecs++;
534: } else {
535: step = TRACE_STEP(sigma.indexOfTargetObj);
536: sigma.trace.add(step);
537: stepResult = Trace.exec(pool, pool
538: .get(sigma.indexOfTargetObj), step, classINV);
539: }
540:
541: if (stepResult.isReqViolating()) {
542: numOfViolations++;
543: sigma.classification = Trace.VIOLATING_TRACE;
544: // record the trace, then clear sigma:
545: collectedTraces.add(sigma);
546: depth = 0;
547: sigma = null;
548: continue;
549: }
550: if (stepResult.isAsmViolating()) {
551: // irrelavant step
552: numOfIrrelevantChecks++;
553: // discarding sigma:
554: depth = 0;
555: sigma = null;
556: continue;
557: }
558: // Else the step was OK.
559: depth++;
560: }
561: // end of main loop
562: }
563:
564: private static final String lineHsep1 = "----------------------------------------------------";
565:
566: /**
567: * To report violating executions found by the engine.
568: */
569: public void report(PrintStream out) {
570:
571: out.println("");
572: if (numOfViolations <= 0)
573: out.println("** NO violation found.");
574: else
575: out.println("** " + numOfViolations + " VIOLATIONS found.");
576: out.println(lineHsep1);
577: out.println("** total attempted execution steps : "
578: + numOfSteps);
579: out.println("** total generated executions : "
580: + numOfExecs);
581: out.println("** number of irrelevant checks : "
582: + numOfIrrelevantChecks);
583: out.println(lineHsep1);
584: out.println("");
585: if (numOfViolations > 0) {
586: int i = 1;
587: for (Trace trace : collectedTraces) {
588: if (trace.classification != Trace.VIOLATING_TRACE)
589: continue;
590: // ELSE it is a violating trace:
591: // Show.show(trace) ;
592: out.println("");
593: out.println("** Violating trace [" + i + "] :");
594: trace.report(pool, classINV, out);
595: i++;
596: }
597: }
598: }
599:
600: private void reportOptions(PrintStream out) {
601: out.println(lineHsep1);
602: out.println("** Engine CONFIGURATION:");
603: out.println("** Max. number of steps : " + maxNumOfSteps);
604: out.println("** Max. execution depth : " + maxExecLength);
605: out.println("** Max. object depth (on creation) : "
606: + maxMkValSeqDepth);
607: out
608: .println("** Max. array/collection size : "
609: + maxArrayLength);
610: if (chooseFieldUpdateProb < 0)
611: out.println("** Field update disabled.");
612: else
613: out.println("** Prob. of updating field : "
614: + chooseFieldUpdateProb);
615: if (generateNullProb < 0)
616: out.println("** Auto generation of NULL disabled.");
617: else
618: out.println("** Prob. of auto generation of NULL : "
619: + generateNullProb);
620: if (passTObjAsParamProb < 0)
621: out
622: .println("** Passing target object as parameter disabled.");
623: else
624: out
625: .println("** Prob. of passing target obj as parameter : "
626: + passTObjAsParamProb);
627: out.println("** Prob. of trying pool before constuctor : "
628: + pickTargetObjFromPoolProb);
629: out.println("** CUT : " + CUT.getName());
630: if (TYVAR0 == null)
631: out.println("** TYVAR0 : " + CUT.getName());
632: else
633: out.println("** TYVAR0 : " + TYVAR0.getName());
634: out.println("** Pool : " + pool.getClass().getName());
635: out.println("** Base domain : "
636: + baseDomain.getClass().getName());
637: out.println("** Interface map : "
638: + interfaceMap.getClass().getName());
639: out.println(lineHsep1);
640: }
641:
642: /**
643: * A <b>main Testing API</b>; this static method will unleash
644: * random testing on a given target class. Internally the method
645: * creates a default test engine and uses it to test the class
646: * C. Options can be passed to customize the engine; then the
647: * engine is run. Violating executions will be reported and saved
648: * in the file C.tr. The method terminates if the underlying test
649: * engine throws a T2Error.
650: *
651: * @param C The class to test. Passing null will print the usage info to the console.
652: * @param p An object pool to be used by the testing engine.
653: * @param dom A base domain to be used by the engine.
654: * @param out The (default) printstream to which the engine will send report.
655: * @param options Options to configure the engine before running
656: * it; see the source code of printUsage in this class to see
657: * available options.
658: *
659: */
660: public static void RndTest(Class C, Pool p, BaseDomain dom,
661: InterfaceMap imap, PrintStream out, String[] options)
662:
663: throws T2Exception {
664:
665: if (C == null) {
666: printUsage(out);
667: return;
668: }
669:
670: out.println("\n" + Message.GREET);
671: try {
672: out.println("** Creating test engine ...");
673: RndEngine E = new RndEngine(C, p, dom, imap);
674: E.configureOptions(options);
675: E.reportOptions(out);
676: out.println("** Testing " + C.getSimpleName() + " ...");
677: long time = System.currentTimeMillis();
678: E.runMe();
679: time = System.currentTimeMillis() - time;
680: out.println("** Time elapsed: " + time + " ms");
681: E.report(out);
682: if (E.collectedTraces.size() > 0) {
683: out.println("** Saving traces...");
684: Trace.save(C, E.collectedTraces);
685: /*
686: out.println("** Loading traces...") ;
687: try {
688: LinkedList<Trace> savedTraces = E.load(C.getSimpleName()) ;
689: out.println("\n\n** REGRESSING the traces.") ;
690: for (Trace trace : savedTraces) {
691: out.println("\n*******") ;
692: trace.simulate(p) ;
693: } ;
694: }
695: catch(IOException e) { } ;
696: */
697: }
698: out.println("** DONE.");
699: } catch (CmdLineParser.OptionException e) {
700: printUsage(out);
701: }
702: }
703:
704: /**
705: * A <b>main Testing API</b>; almost as the other RndTest. However,
706: * this one internally catches exceptions and logs the corresponding
707: * messages and tracestack. Report will be sent by default to the console.
708: *
709: * @param args List of options, including the name of the target class;
710: * the latter has to be the first in the list; if the list is empty,
711: * help
712: *
713: * @see RndEngine#RndTest(Class,Pool,BaseDomain,InterfaceMap,
714: * PrintStream,String[])
715: *
716: */
717:
718: public static void RndTest(Pool p, BaseDomain dom,
719: InterfaceMap imap, String[] args) {
720: Class CUT = null;
721:
722: // Get the target class (CUT):
723: if (args.length > 0)
724:
725: // System.out.println("## " + args[0]) ;
726:
727: try {
728: CUT = Class.forName(args[0]);
729: } catch (Exception e) {
730: logger.log(Level.SEVERE,
731: "\n## ABORT. T2 fails to get class " + args[0]);
732: return;
733: }
734:
735: // Unleash the test engine:
736:
737: // System.out.println("## " + CUT.getName()) ;
738:
739: try {
740: RndTest(CUT, p, dom, imap, System.out, args);
741: } catch (Throwable e) {
742: StringWriter st = new StringWriter();
743: PrintWriter pw = new PrintWriter(st);
744: e.printStackTrace(pw);
745: pw.flush();
746: logger.log(Level.SEVERE, e.getMessage() + "\n"
747: + "Strack trace:\n" + st.toString());
748: }
749: }
750:
751: /**
752: * Short descriptions of available options.
753: */
754: protected static String[] optionsShortDesc = {
755: " -l int Maximum execution depth.",
756: " -d int Maximum object depth (at the creation).",
757: " -n int Maximum number of execution steps afterwhich RT2 stops.",
758: " -v int Maximum number of violations afterwhich RT2 stops.",
759: " --elemty=name The name of the type of elements of Collections.",
760: " --pubonly Limit the test to public methods members.",
761: " --ownclassonly Limit the test to members declared by the target class.",
762: " --nullprob=value The probability to generate null. Use negative value to surpress." };
763:
764: /**
765: * We'll have to filter methodsCanAcceptCUTinParam again, to take
766: * into account the elemty option.
767: */
768: private boolean canAcceptCUTAsParameter(Method m) {
769: Class[] paramTypes = m.getParameterTypes();
770: boolean found = false;
771: for (int i = 0; i < paramTypes.length && !found; i++)
772:
773: found = paramTypes[i].isAssignableFrom(CUT)
774: && (TYVAR0 == null || !paramTypes[i].getName()
775: .equals("java.lang.Object"));
776:
777: return found;
778: }
779:
780: /**
781: * Utiltity to configure the engine from options passed as command
782: * line arguments.
783: */
784: private void configureOptions(String[] args)
785: throws CmdLineParser.OptionException {
786: if (args == null)
787: return;
788: CmdLineParser parser = new CmdLineParser();
789: CmdLineParser.Option execLengthO = parser.addIntegerOption('l',
790: "lenexec");
791: CmdLineParser.Option objDepthO = parser.addIntegerOption('d',
792: "depthobj");
793: CmdLineParser.Option maxNumOfStepsO = parser.addIntegerOption(
794: 'n', "nmax");
795: CmdLineParser.Option maxNumOfViolO = parser.addIntegerOption(
796: 'v', "violmax");
797: CmdLineParser.Option typeOfColElementO = parser
798: .addStringOption("elemty");
799: CmdLineParser.Option pubOnlyO = parser
800: .addBooleanOption("pubonly");
801: CmdLineParser.Option ownClassOnlyO = parser
802: .addBooleanOption("ownclassonly");
803: CmdLineParser.Option nullProbO = parser
804: .addDoubleOption("nullprob");
805:
806: parser.parse(args);
807:
808: // String [] otherArgs = parser.getRemainingArgs();
809: //
810: // if (otherArgs == null || otherArgs.length<=0)
811: // throw new CmdLineParser.OptionException() ;
812:
813: maxNumOfSteps = (Integer) parser.getOptionValue(maxNumOfStepsO,
814: maxNumOfSteps);
815:
816: maxExecLength = (Integer) parser.getOptionValue(execLengthO,
817: maxExecLength);
818:
819: maxNumViolations = (Integer) parser.getOptionValue(
820: maxNumOfViolO, maxNumViolations);
821:
822: maxMkValSeqDepth = (Integer) parser.getOptionValue(objDepthO,
823: maxMkValSeqDepth);
824:
825: if (typeOfColElementO != null)
826:
827: try {
828: TYVAR0 = Class.forName((String) parser
829: .getOptionValue(typeOfColElementO));
830: LinkedList<Method> toBeRemoved = new LinkedList<Method>();
831: for (Method m : methodsCanAcceptCUTinParam)
832: if (!canAcceptCUTAsParameter(m))
833: toBeRemoved.add(m);
834: for (Method m : toBeRemoved)
835: methodsCanAcceptCUTinParam.remove(m);
836: }
837:
838: catch (Exception e) {
839: }
840:
841: // debug:
842: // for (Method m : methodsCanAcceptCUTinParam) System.out.println(">>" + m.getName()) ;
843:
844: if (pubOnlyO != null) {
845: ReflUtil.removeNonPubMethods(methodsIPs);
846: ReflUtil.removeNonPubMethods(methodsCanAcceptCUTinParam);
847: ReflUtil.removeNonPubMethods(methodsCanAcceptCUTinRec);
848: ReflUtil.removeNonPubFields(fieldsIPs);
849: ReflUtil.removeNonPubCons(consIPs);
850: }
851:
852: if (ownClassOnlyO != null) {
853: ReflUtil.removeNonCMethods(methodsIPs, CUT);
854: ReflUtil.removeNonCMethods(methodsCanAcceptCUTinParam, CUT);
855: ReflUtil.removeNonCMethods(methodsCanAcceptCUTinRec, CUT);
856: ReflUtil.removeNonCFields(fieldsIPs, CUT);
857: }
858:
859: double p = (Double) parser.getOptionValue(nullProbO,
860: (double) generateNullProb);
861: generateNullProb = (float) p;
862:
863: }
864:
865: /**
866: * Print usage help to the console.
867: */
868: public static void printUsage(PrintStream out) {
869: out.println("");
870: out.println(lineHsep1);
871: out.println("USAGE.");
872: out.println("");
873: out.println(" java ... RT2 CUT Option*");
874: out.println("");
875: out.println(lineHsep1);
876: out.println("");
877: out.println("...: java options, e.g. class path.");
878: out.println("CUT: the name of the target class.");
879: out.println("");
880: out.println("Available options:");
881: out.println("");
882: for (int i = 0; i < optionsShortDesc.length; i++)
883: out.println(optionsShortDesc[i]);
884: out.println(lineHsep1);
885: }
886:
887: /**
888: * Just for testing this class.
889: */
890: static public void main(String[] args) {
891:
892: String[] options = { "U2.T2.Engine" };
893:
894: RndTest(new Pool(), new BaseDomain0(), new InterfaceMap0(),
895: options);
896:
897: options[0] = "U2.T2.MyPerson";
898:
899: RndTest(new Pool(), new BaseDomain0(), new InterfaceMap0(),
900: options);
901:
902: }
903:
904: }
|