001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2007 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.testing;
010:
011: import j2me.lang.CharSequence;
012: import javolution.context.LogContext;
013: import javolution.context.ObjectFactory;
014: import javolution.text.TextBuilder;
015:
016: /**
017: * <p> This class represents a logging context specialized for testing.</p>
018: *
019: * <p> Custom implementations may output results in varied form (e.g. tabular)
020: * and/or perform all kind of measurements (e.g. {@link TimeContext timing},
021: * memory usage, etc.) For example:[code]
022: * TestContext tabularLog = new TestContext() { ... } // Outputs to spreadsheet.
023: * TestContext.enter(tabularLog);
024: * try {
025: * TestContext.run(new myTestSuite());
026: * ...
027: * } finally {
028: * TestContext.exit();
029: * }[/code] </p>
030: *
031: * <p> For automatic regression tests, developers may use the
032: * {@link #REGRESSION} implementation which does not perform any
033: * logging but raises an {@link AssertionException} when an assertion fails.
034: * For example:[code]
035: * TestContext.enter(TestContext.REGRESSION);
036: * try {
037: * TestContext.run(new myTestSuite()); // AssertionError is one assertion fails.
038: * ...
039: * } finally {
040: * TestContext.exit();
041: * }[/code] </p>
042: *
043: *
044: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
045: * @version 5.2, August 5, 2007
046: * @see TestCase
047: */
048: public abstract class TestContext extends LogContext {
049:
050: /**
051: * Holds an implementation which does not perform any logging but raises
052: * an {@link AssertionException} when an assertion fails.
053: * This implementation can be used for automatic regression tests.
054: */
055: public static final Class/*<TestContext>*/REGRESSION = Regression.CLASS;
056:
057: /**
058: * Runs an individual test case (possibly multiple times) and logs the
059: * results. If the current logging context is a test context then
060: * {@link TestContext#doTest(TestCase)} is called; otherwise the name of
061: * test case is logged (info), the test case is excuted once
062: * and the test results are written to the current logging context.
063: *
064: * @param testCase the test case being executed.
065: */
066: public static void test(TestCase testCase) {
067: LogContext ctx = (LogContext) LogContext.getCurrent();
068: if (ctx instanceof TestContext) {
069: ((TestContext) ctx).doTest(testCase);
070: } else { // Not a test context.
071: if (ctx.isInfoLogged()) {
072: ctx.logInfo(testCase.toString());
073: }
074: testCase.prepare();
075: try {
076: testCase.execute();
077: testCase.validate();
078: } finally {
079: testCase.cleanup();
080: }
081: }
082: }
083:
084: /**
085: * Checks the equality of both objects specified. If the current logging
086: * context is a test context then
087: * {@link TestContext#doAssertEquals(String, Object, Object)} is called;
088: * otherwise an error message is logged only if the assert fails.
089: *
090: * @param message the message displayed if assert fails (can be <code>null</code>)
091: * @param expected the expected result (can be <code>null</code>).
092: * @param actual the actual result (can be <code>null</code>).
093: * @return <code>true</code> if both expected and actual are equal;
094: * <code>false</code> otherwise.
095: */
096: public static boolean assertEquals(String message, Object expected,
097: Object actual) {
098: LogContext ctx = (LogContext) LogContext.getCurrent();
099: if (ctx instanceof TestContext) {
100: return ((TestContext) ctx).doAssertEquals(message,
101: expected, actual);
102: } else { // Not a test context.
103: if (((expected == null) && (actual != null))
104: || ((expected != null) && (!expected.equals(actual)))) {
105: if (ctx.isErrorLogged()) {
106: TextBuilder tmp = TextBuilder.newInstance();
107: if (message != null) {
108: tmp.append(message).append(": ");
109: }
110: tmp.append(expected);
111: tmp.append(" expected but found ");
112: tmp.append(actual);
113: ctx.logError(null, tmp);
114: TextBuilder.recycle(tmp);
115: }
116: return false;
117: }
118: return true;
119: }
120: }
121:
122: /**
123: * Convenience method equivalent to <code>assertEquals(null, expected, value)</code>.
124: *
125: * @param expected the expected result (can be <code>null</code>).
126: * @param actual the actual result (can be <code>null</code>).
127: */
128: public static boolean assertEquals(Object expected, Object actual) {
129: return TestContext.assertEquals(null, expected, actual);
130: }
131:
132: /**
133: * Convenience method equivalent to <code>assertEquals(message, true, value)</code>.
134: *
135: * @param message the message displayed if assert fails (can be <code>null</code>)
136: * @param actual the actual value.
137: * @return <code>true</code> if actual is <code>true</code>;
138: * <code>false</code> otherwise.
139: */
140: public static boolean assertTrue(String message, boolean actual) {
141: return TestContext.assertEquals(message, TRUE, actual ? TRUE
142: : FALSE);
143: }
144:
145: private static final Boolean TRUE = new Boolean(true);
146: private static final Boolean FALSE = new Boolean(false);
147:
148: /**
149: * Convenience method equivalent to <code>assertEquals(null, true, value)</code>.
150: *
151: * @param actual the actual value.
152: * @return <code>true</code> if actual is <code>true</code>;
153: * <code>false</code> otherwise.
154: */
155: public static boolean assertTrue(boolean actual) {
156: return TestContext.assertEquals(null, TRUE, actual ? TRUE
157: : FALSE);
158: }
159:
160: /**
161: * Executes the specified test case and logs the results.
162: *
163: * @param testCase the test case being executed.
164: */
165: public abstract void doTest(TestCase testCase);
166:
167: /**
168: * Asserts that two objects are equal.
169: *
170: * @param message the message displayed if assert fails (can be <code>null</code>)
171: * @param expected the expected result (can be <code>null</code>).
172: * @param actual the actual result (can be <code>null</code>).
173: * @return <code>true</code> if both expected and actual are equal;
174: * <code>false</code> otherwise.
175: */
176: public abstract boolean doAssertEquals(String message,
177: Object expected, Object actual);
178:
179: // TestContext implementation with no output (just validation).
180: private static final class Regression extends TestContext {
181:
182: private static final Class CLASS = new Regression().getClass();
183:
184: // Overrides.
185: public void doTest(TestCase testCase) {
186: testCase.prepare();
187: try {
188: testCase.execute();
189: testCase.validate();
190: } finally {
191: testCase.cleanup();
192: }
193: }
194:
195: // Overrides.
196: public boolean doAssertEquals(String message, Object expected,
197: Object actual) {
198: if (((expected == null) && (actual != null))
199: || ((expected != null) && (!expected.equals(actual))))
200: throw new AssertionException(message, expected, actual);
201: return true;
202: }
203:
204: public boolean isErrorLogged() {
205: return false;
206: }
207:
208: public boolean isInfoLogged() {
209: return false;
210: }
211:
212: public boolean isWarningLogged() {
213: return false;
214: }
215:
216: public void logError(Throwable error, CharSequence message) {
217: }
218:
219: public void logInfo(CharSequence message) {
220: }
221:
222: public void logWarning(CharSequence message) {
223: }
224: }
225:
226: // Allows instances of private classes to be factory produced.
227: static {
228: ObjectFactory.setInstance(new ObjectFactory() {
229: protected Object create() {
230: return new Regression();
231: }
232: }, Regression.CLASS);
233: }
234:
235: }
|