001: /**
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */package com.tc.test;
005:
006: import org.apache.commons.lang.exception.ExceptionUtils;
007:
008: import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
009: import EDU.oswego.cs.dl.util.concurrent.SynchronizedRef;
010:
011: import com.tc.exception.TCRuntimeException;
012: import com.tc.logging.TCLogging;
013: import com.tc.test.collections.CollectionAssert;
014: import com.tc.text.Banner;
015: import com.tc.util.Assert;
016: import com.tc.util.EqualityComparator;
017: import com.tc.util.SameObjectEqualityComparator;
018: import com.tc.util.StandardStringifier;
019: import com.tc.util.Stringifier;
020: import com.tc.util.TCTimerImpl;
021: import com.tc.util.diff.Difference;
022: import com.tc.util.diff.DifferenceBuilder;
023: import com.tc.util.diff.Differenceable;
024: import com.tc.util.runtime.Os;
025: import com.tc.util.runtime.ThreadDump;
026:
027: import java.io.ByteArrayInputStream;
028: import java.io.ByteArrayOutputStream;
029: import java.io.File;
030: import java.io.FileWriter;
031: import java.io.IOException;
032: import java.io.ObjectInputStream;
033: import java.io.ObjectOutputStream;
034: import java.io.PrintWriter;
035: import java.text.DateFormat;
036: import java.text.ParseException;
037: import java.text.SimpleDateFormat;
038: import java.util.Arrays;
039: import java.util.Comparator;
040: import java.util.Date;
041: import java.util.Hashtable;
042: import java.util.List;
043: import java.util.Map;
044: import java.util.Timer;
045: import java.util.TimerTask;
046:
047: import junit.framework.AssertionFailedError;
048: import junit.framework.TestCase;
049:
050: /**
051: * A base for all Terracotta tests.
052: */
053: public class TCTestCase extends TestCase {
054:
055: private static final long DEFAULT_TIMEOUT_THRESHOLD = 60000;
056: private static final DateFormat DATE_FORMAT = new SimpleDateFormat(
057: "yyyy-MM-dd");
058:
059: private final SynchronizedRef beforeTimeoutException = new SynchronizedRef(
060: null);
061:
062: private DataDirectoryHelper dataDirectoryHelper;
063: private TempDirectoryHelper tempDirectoryHelper;
064:
065: private Date allDisabledUntil;
066: private final Map disabledUntil = new Hashtable();
067:
068: // This stuff is static since Junit new()'s up an instance of the test case for each test method,
069: // and the timeout covers the entire test case (ie. all methods). It wouldn't be very effective to start
070: // the timer for each test method given this
071: private static final Timer timeoutTimer = new TCTimerImpl(
072: "Timeout Thread", true);
073: private static final SynchronizedBoolean timeoutTaskAdded = new SynchronizedBoolean(
074: false);
075:
076: private static boolean printedProcess = false;
077:
078: // If you want to customize this, you have to do it in the constructor of your test case (setUp() is too late)
079: private long timeoutThreshold = DEFAULT_TIMEOUT_THRESHOLD;
080:
081: // controls for thread dumping.
082: private boolean dumpThreadsOnTimeout = true;
083: private int numThreadDumps = 3;
084: private long dumpInterval = 500;
085:
086: // a way to ensure that system clock moves forward...
087: private long previousSystemMillis = 0;
088:
089: public TCTestCase() {
090: super ();
091: init();
092: }
093:
094: public TCTestCase(String arg0) {
095: super (arg0);
096: init();
097: }
098:
099: private void init() {
100: TCLogging.disableLocking();
101: printOutCurrentJavaProcesses();
102: }
103:
104: // called by timer thread (ie. NOT the main thread of test case)
105: private void timeoutCallback() {
106: String bar = "***************************************";
107: System.err.println("\n" + bar
108: + "\n+ TCTestCase timeout alarm going off at "
109: + new Date() + "\n" + bar + "\n");
110: System.err.flush();
111:
112: doDumpServerDetails();
113: if (dumpThreadsOnTimeout) {
114: try {
115: doThreadDump();
116: } catch (Throwable t) {
117: // don't fail the test b/c of this
118: t.printStackTrace();
119: }
120: }
121:
122: try {
123: beforeTimeout();
124: } catch (Throwable t) {
125: this .beforeTimeoutException.set(t);
126: }
127: }
128:
129: protected void doDumpServerDetails() {
130: // NOP - Overridden by subclasses
131: }
132:
133: // override this method if you want to do something before your test times out
134: protected void beforeTimeout() throws Throwable {
135: if (false)
136: throw new AssertionError(); // silence compiler warning
137: }
138:
139: public void runBare() throws Throwable {
140: if (isAllDisabled()) {
141: System.out.println("NOTE: ALL tests in "
142: + this .getClass().getName()
143: + " are disabled until " + this .allDisabledUntil);
144: System.out.flush();
145: return;
146: }
147:
148: final String testMethod = getName();
149: if (isTestDisabled(testMethod)) {
150: System.out.println("NOTE: Test method " + testMethod
151: + "() is disabled until "
152: + this .disabledUntil.get(testMethod));
153: System.out.flush();
154: return;
155: }
156:
157: // don't move this stuff to runTest(), you want the timeout timer to catch hangs in setUp() too.
158: // Yes it means you can't customize the timeout threshold in setUp() -- take a deep breath and
159: // set your value in the constructor of your test case instead of setUp()
160: if (timeoutTaskAdded.commit(false, true)) {
161: scheduleTimeoutTask();
162: }
163:
164: Throwable testException = null;
165: try {
166: super .runBare();
167: } catch (Throwable t) {
168: testException = t;
169: }
170:
171: Throwable exceptionInTimeoutCallback = (Throwable) beforeTimeoutException
172: .get();
173:
174: // favor the "real" exception to make test fail. If there was a exception in the timeout callback,
175: // make that able to fail the test too
176: if (testException != null) {
177: if (exceptionInTimeoutCallback != null) {
178: exceptionInTimeoutCallback.printStackTrace();
179: }
180: throw testException;
181: }
182:
183: if (exceptionInTimeoutCallback != null) {
184: throw exceptionInTimeoutCallback;
185: }
186:
187: // no errors -- woo-hoo!
188: return;
189: }
190:
191: private void printOutCurrentJavaProcesses() {
192: if (printedProcess)
193: return;
194: printedProcess = true;
195: PrintWriter out = null;
196: try {
197: out = new PrintWriter(new FileWriter(this
198: .getTempFile("javaprocesses.txt")));
199: out.println(ProcessInfo.ps_grep_java());
200: } catch (Exception e) {
201: throw new RuntimeException(e);
202: } finally {
203: if (out != null)
204: out.close();
205: }
206: }
207:
208: private void scheduleTimeoutTask() throws IOException {
209: // enforce some sanity
210: final int MINIMUM = 30;
211: long junitTimeout = this .getTimeoutValueInSeconds();
212:
213: if (junitTimeout < MINIMUM) {
214: throw new IllegalArgumentException(
215: "Junit timeout cannot be less than " + MINIMUM
216: + " seconds");
217: }
218:
219: final int MIN_THRESH = 15000;
220: junitTimeout *= 1000;
221: if ((junitTimeout - timeoutThreshold) < MIN_THRESH) {
222: System.err
223: .println("ERROR: Cannot apply timeout threshold of "
224: + timeoutThreshold
225: + ", using "
226: + MIN_THRESH + " instead");
227: System.err.flush();
228: timeoutThreshold = MIN_THRESH;
229: }
230:
231: long delay = junitTimeout - timeoutThreshold;
232:
233: timeoutTimer.schedule(new TimerTask() {
234: public void run() {
235: timeoutCallback();
236: }
237: }, delay);
238: }
239:
240: public void setThreadDumpInterval(long interval) {
241: this .dumpInterval = interval;
242: }
243:
244: public void setDumpThreadsOnTimeout(boolean dump) {
245: this .dumpThreadsOnTimeout = dump;
246: }
247:
248: public void setNumThreadDumps(int dumps) {
249: this .numThreadDumps = dumps;
250: }
251:
252: public void setTimeoutThreshold(long threshold) {
253: this .timeoutThreshold = threshold;
254: }
255:
256: protected final synchronized TempDirectoryHelper getTempDirectoryHelper() {
257: if (tempDirectoryHelper == null) {
258: try {
259: tempDirectoryHelper = new TempDirectoryHelper(
260: getClass(), cleanTempDir());
261: } catch (IOException ioe) {
262: throw new TCRuntimeException(
263: "Can't get configuration for temp directory",
264: ioe);
265: }
266: }
267:
268: return tempDirectoryHelper;
269: }
270:
271: protected boolean cleanTempDir() {
272: return true;
273: }
274:
275: protected final synchronized DataDirectoryHelper getDataDirectoryHelper() {
276: if (dataDirectoryHelper == null) {
277: try {
278: dataDirectoryHelper = new DataDirectoryHelper(
279: getClass());
280: } catch (IOException ioe) {
281: throw new TCRuntimeException(ioe.getLocalizedMessage(),
282: ioe);
283: }
284: }
285:
286: return dataDirectoryHelper;
287: }
288:
289: protected final File getDataDirectory() throws IOException {
290: return getDataDirectoryHelper().getDirectory();
291: }
292:
293: protected final File getDataFile(String fileName)
294: throws IOException {
295: return getDataDirectoryHelper().getFile(fileName);
296: }
297:
298: protected final File getTempDirectory() throws IOException {
299: return getTempDirectoryHelper().getDirectory();
300: }
301:
302: protected final File getTempFile(String fileName)
303: throws IOException {
304: return getTempDirectoryHelper().getFile(fileName);
305: }
306:
307: /**
308: * Disable ALL tests until the given date. This method should be called in the constructor of your unit test
309: */
310: protected final void disableAllUntil(Date theDate) {
311: Assert.eval(theDate != null);
312: this .allDisabledUntil = theDate;
313: Banner.warnBanner(this .getClass().getName()
314: + " disabled until " + theDate);
315: }
316:
317: /**
318: * Disable ALL tests until the given date. This method should be called in the constructor of your unit test
319: */
320: protected final void disableAllUntil(String theDate) {
321: disableAllUntil(parseDate(theDate));
322: }
323:
324: /**
325: * Disable ALL tests until the given date. This method should be called in the constructor of your unit test Only
326: * specified platforms ("windows", "linux", "solaris") will take effect.
327: */
328: protected final void disableAllUntil(String theDate,
329: String[] platforms) {
330: List platform_list = Arrays.asList(platforms);
331: String platform = TestConfigObject.getInstance().platform();
332: if (platform_list.contains(platform)) {
333: disableAllUntil(parseDate(theDate));
334: }
335: }
336:
337: /**
338: * Disable the given test method until the given date. This method should be called in the constructor of your unit
339: * test
340: */
341: protected final void disableTestUntil(String method, String date) {
342: this .disabledUntil.put(method, parseDate(date));
343: }
344:
345: /**
346: * Disable the given test method until the given date. This method should be called in the constructor of your unit
347: * test. Only specified platforms ("windows", "linux", "solaris") take effect.
348: */
349: protected final void disableTestUntil(String method, String date,
350: String[] platforms) {
351: List platform_list = Arrays.asList(platforms);
352: String platform = TestConfigObject.getInstance().platform();
353: if (platform_list.contains(platform)) {
354: this .disabledUntil.put(method, parseDate(date));
355: }
356: }
357:
358: /**
359: * Disable the given test method until the given date. This method should be called in the constructor of your unit
360: * test
361: */
362: protected final void disableTestUntil(String method, Date date) {
363: this .disabledUntil.put(method, date);
364: }
365:
366: protected final void assertSameOrdered(Object one, Object two) {
367: assertEqualsOrdered(one, two,
368: SameObjectEqualityComparator.INSTANCE);
369: }
370:
371: protected final void assertEqualsOrdered(Object one, Object two) {
372: CollectionAssert.assertEqualOrdered(one, two);
373: }
374:
375: protected final void assertEqualsOrdered(Object one, Object two,
376: EqualityComparator comparator) {
377: CollectionAssert.assertEqualOrdered(one, two, comparator);
378: }
379:
380: protected final void assertSameUnordered(Object one, Object two) {
381: assertEqualsUnordered(one, two,
382: SameObjectEqualityComparator.INSTANCE);
383: }
384:
385: protected final void assertEqualsUnordered(Object one, Object two) {
386: CollectionAssert.assertEqualUnordered(one, two);
387: }
388:
389: protected final void assertEqualsUnordered(Object one, Object two,
390: EqualityComparator comparator) {
391: CollectionAssert.assertEqualUnordered(one, two, comparator);
392: }
393:
394: protected final void assertSameUnorderedUncounted(Object one,
395: Object two) {
396: assertEqualsUnorderedUncounted(one, two,
397: SameObjectEqualityComparator.INSTANCE);
398: }
399:
400: protected final void assertEqualsUnorderedUncounted(Object one,
401: Object two) {
402: CollectionAssert.assertEqualUnorderedUncounted(one, two);
403: }
404:
405: protected final void assertEqualsUnorderedUncounted(Object one,
406: Object two, EqualityComparator comparator) {
407: CollectionAssert.assertEqualUnorderedUncounted(one, two,
408: comparator);
409: }
410:
411: protected final void assertEqualsVerbose(Object one, Object two) {
412: assertEqualsVerbose(null, one, two,
413: StandardStringifier.INSTANCE, false);
414: }
415:
416: protected final void assertEqualsVerbose(Object one, Object two,
417: Stringifier stringifier) {
418: assertEqualsVerbose(null, one, two, stringifier, false);
419: }
420:
421: protected final void assertEqualsVerbose(Object one, Object two,
422: boolean showObjects) {
423: assertEqualsVerbose(null, one, two,
424: StandardStringifier.INSTANCE, showObjects);
425: }
426:
427: protected final void assertEqualsVerbose(String message,
428: Object one, Object two) {
429: assertEqualsVerbose(message, one, two,
430: StandardStringifier.INSTANCE, false);
431: }
432:
433: protected final void assertEqualsVerbose(String message,
434: Object one, Object two, Stringifier stringifier) {
435: assertEqualsVerbose(message, one, two, stringifier, false);
436: }
437:
438: protected final void assertEqualsVerbose(String message,
439: Object one, Object two, boolean showObjects) {
440: assertEqualsVerbose(message, one, two,
441: StandardStringifier.INSTANCE, showObjects);
442: }
443:
444: protected final void assertContainsIgnoreCase(String expected,
445: String actual) {
446: assertContainsIgnoreCase(null, expected, actual);
447: }
448:
449: protected final void assertContainsIgnoreCase(String message,
450: String expected, String actual) {
451: assertContains(message, expected != null ? expected
452: .toLowerCase() : null, actual != null ? actual
453: .toLowerCase() : null);
454: }
455:
456: protected final void assertContains(String expected, String actual) {
457: assertContains(null, expected, actual);
458: }
459:
460: protected final void assertContains(String message,
461: String expected, String actual) {
462: if ((expected == null) != (actual == null)) {
463: message = (message == null ? "" : message + ": ");
464: fail(message
465: + "Expected was "
466: + (expected == null ? "<null>" : "'" + expected
467: + "'") + ", but actual was "
468: + (actual == null ? "<null>" : "'" + actual + "'"));
469: }
470:
471: if (expected != null) {
472: if (actual.indexOf(expected) < 0) {
473: message = (message == null ? "" : message + ": ");
474: fail(message + "Actual string '" + actual
475: + "' does not contain expected string '"
476: + expected + "'");
477: }
478: }
479: }
480:
481: protected final void assertEqualsVerbose(String message,
482: Object one, Object two, Stringifier stringifier,
483: boolean showObjects) {
484: if (one != null && two != null
485: && (one instanceof Differenceable)
486: && (two instanceof Differenceable)
487: && (one.getClass().equals(two.getClass()))
488: && (!one.equals(two))) {
489: Difference[] differences = DifferenceBuilder
490: .getDifferencesAsArray((Differenceable) one,
491: (Differenceable) two);
492: Assert.eval(differences.length > 0); // since we know they're not equal
493:
494: StringBuffer descrip = new StringBuffer();
495: descrip.append((message != null ? (message + ": ") : "")
496: + "objects not equal");
497: descrip.append(DifferenceBuilder.describeDifferences(
498: (Differenceable) one, (Differenceable) two));
499:
500: if (showObjects) {
501: descrip.append("\nexpected:\n");
502: descrip.append(one.toString());
503: descrip.append("\nbut was:\n");
504: descrip.append(two.toString());
505: descrip.append("\n");
506: }
507:
508: throw new AssertionFailedError(descrip.toString());
509: } else {
510: assertEquals(one, two);
511: }
512: }
513:
514: protected final void fail(Throwable t) {
515: fail("FAILURE", t);
516: }
517:
518: protected final void fail(String message, Throwable t) {
519: fail((message == null ? "" : (message + "\n")) + "Exception:\n"
520: + ExceptionUtils.getFullStackTrace(t));
521: }
522:
523: private Date parseDate(String date) {
524: try {
525: return DATE_FORMAT.parse(date);
526: } catch (ParseException e) {
527: // throwing runtime exception should cause each test case to fail
528: // (provided you're disabling from the constructor
529: // as directed)
530: throw new TCRuntimeException(e);
531: }
532: }
533:
534: protected final boolean isAllDisabled() {
535: return this .allDisabledUntil != null
536: && new Date().before(this .allDisabledUntil);
537: }
538:
539: private boolean isTestDisabled(String testMethod) {
540: Date until = (Date) disabledUntil.get(testMethod);
541: return until != null && new Date().before(until);
542: }
543:
544: protected void checkComparator(Object smaller, Object bigger,
545: Object equalToBigger, Comparator c) {
546: // test null's
547: assertTrue(c.compare(null, bigger) < 0);
548: assertTrue(c.compare(bigger, null) > 0);
549: assertTrue(c.compare(null, null) == 0);
550:
551: // test less-than
552: assertTrue(c.compare(smaller, bigger) < 0);
553:
554: // test greater-than
555: assertTrue(c.compare(bigger, smaller) > 0);
556:
557: // test equal
558: assertTrue(c.compare(bigger, equalToBigger) == 0);
559: assertTrue(c.compare(equalToBigger, bigger) == 0);
560: }
561:
562: protected void assertNotEquals(int i1, int i2) {
563: assertFalse("Values are equal: " + i1, i1 == i2);
564: }
565:
566: protected void assertEquals(byte[] b1, byte[] b2) {
567: boolean rv = (b1 == null) ? b2 == null : Arrays.equals(b1, b2);
568: assertTrue("Values are not equals", rv);
569: }
570:
571: protected void assertNotEquals(Object o1, Object o2) {
572: assertFalse("Values are equal: " + o1 + ", " + o2, o1 == o2);
573: if (o1 != null && o2 != null) {
574: assertFalse("Values are equal: " + o1 + ", " + o2, o1
575: .equals(o2));
576: assertFalse("Values are equal: " + o1 + ", " + o2, o2
577: .equals(o1));
578: }
579: }
580:
581: protected void assertSerializable(Object obj) {
582: assertSerializable(obj, true, true);
583: }
584:
585: protected void assertSerializable(Object obj, boolean checkEquals,
586: boolean checkHashCode) {
587: assertNotNull(obj);
588: ByteArrayOutputStream baos = new ByteArrayOutputStream();
589: ObjectOutputStream oos;
590: Object deserializedObj = null;
591: try {
592: oos = new ObjectOutputStream(baos);
593: oos.writeObject(obj);
594: oos.close();
595: ObjectInputStream ois = new ObjectInputStream(
596: new ByteArrayInputStream(baos.toByteArray()));
597: deserializedObj = ois.readObject();
598: } catch (IOException ioe) {
599: throw Assert.failure("Object failed to serialize", ioe);
600: } catch (ClassNotFoundException cnfe) {
601: throw Assert.failure("Object failed to serialize", cnfe);
602: }
603: assertNotNull(obj);
604: if (checkEquals) {
605: assertEquals(
606: "Object and [de]serialized object failed equals() comparison",
607: obj, deserializedObj);
608: }
609: if (checkHashCode) {
610: assertEquals(
611: "Object and [de]serialized object failed hashCode() comparison",
612: obj.hashCode(), deserializedObj.hashCode());
613: }
614: }
615:
616: protected synchronized void assertTimeDirection() {
617: long currentMillis = System.currentTimeMillis();
618: assertTrue("System Clock Moved Backwards! [current="
619: + currentMillis + ", previous=" + previousSystemMillis
620: + "]", currentMillis >= previousSystemMillis);
621: previousSystemMillis = currentMillis;
622: }
623:
624: private void doThreadDump() {
625: if (!Os.isUnix()) {
626: ThreadDump.dumpThreadsMany(numThreadDumps, dumpInterval);
627: } else {
628: ThreadDump.dumpProcessGroupMany(numThreadDumps,
629: dumpInterval);
630: }
631: }
632:
633: /**
634: * Returns the timeout value
635: */
636: public int getTimeoutValueInSeconds() {
637: return TestConfigObject.getInstance()
638: .getJunitTimeoutInSeconds();
639: }
640:
641: protected int getThreadDumpCount() {
642: return numThreadDumps;
643: }
644:
645: protected long getThreadDumpInterval() {
646: return dumpInterval;
647: }
648: }
|