001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Anton Luht, Alexander T. Simbirtsev
019: * @version $Revision$
020: */package javax.swing;
021:
022: import java.awt.AWTException;
023: import java.awt.EventQueue;
024: import java.awt.Robot;
025: import java.lang.reflect.Method;
026: import junit.framework.TestCase;
027:
028: public abstract class RobotTestCase extends TestCase {
029: /**
030: * Framework for running JUnit tests that consist of sequence of assertions &
031: * actions. The only visible method in it is static run(Object o, String[]
032: * sequence) It accepts an object and a sequence of methods that should be
033: * called on that object. Methods can be of two types: - public void <method
034: * name>() - action - public boolean <method name>() - assertion
035: *
036: * Runner checks that all methods are actions or assertions (methods are
037: * obtained using Class.getMethod() method) and calls
038: * those methods of the specified objects one by one in cycle in AWT dispatch thread using invokeAndWait()
039: * - if method is an
040: * assertion, it is called in loop max ASSERTION_RETRIES times in ASSERTION_RETRY_TIMEOUT seconds interval
041: * until it returns true or max number of attempts is reached.
042: *
043: * - if method is an action, it is just called as is
044: *
045: * If methods sequence passed successfully and in the proper time frame, the
046: * returned value of run(..) is true in any other case (wrong methods sequence
047: * or a timeout or an unhandled exception in methods loop execution) returned
048: * value is false.
049: *
050: * Please note that the test can hang if one of assertions or actions hangs.
051: *
052: * The example of call is:
053: *
054: * RobotRunner.run(this, new String[] {"assertion0001", "action0001",
055: * "assertion0002", "action0002"});
056: */
057: public static class RobotRunner {
058: private static class RunnableMethod implements Runnable {
059: private final Object target;
060:
061: private final Method method;
062:
063: private Object result;
064:
065: public Object getResult() {
066: return result;
067: }
068:
069: RunnableMethod(final Object target, final Method method) {
070: this .target = target;
071: this .method = method;
072: }
073:
074: public void run() {
075: try {
076: result = method.invoke(target);
077: } catch (Throwable e) {
078: e.printStackTrace();
079: result = null;
080: }
081: }
082: }
083:
084: private static int assertionRetryTimeout = 3000;
085:
086: private static int numAssertionRetries = 5;
087:
088: private static Object testCase;
089:
090: private RobotRunner() {
091: }
092:
093: public static void setTestCase(final Object test) {
094: testCase = test;
095: }
096:
097: public static synchronized boolean run(final String[] sequence) {
098: if (sequence == null || sequence.length == 0) {
099: return false;
100: }
101: if (testCase == null) {
102: return false;
103: }
104: try {
105: for (int i = 0; i < sequence.length; i++) {
106: Method m = testCase.getClass().getMethod(
107: sequence[i], new Class[] {});
108: if ((m.getReturnType() != boolean.class)
109: && (m.getReturnType() != void.class)) { // assertion
110: System.err.println("unknown return type: "
111: + m.getReturnType() + " for method '"
112: + sequence[i] + "' in test sequence");
113: return false;
114: }
115: }
116: //ok then - all methods are correct, we can start
117: for (int i = 0; i < sequence.length; i++) {
118: Method m = testCase.getClass().getMethod(
119: sequence[i], new Class[] {});
120: RunnableMethod rm = new RunnableMethod(testCase, m);
121: if (m.getReturnType() == boolean.class) { // assertion
122: Boolean result = Boolean.FALSE;
123: for (int j = 0; j < numAssertionRetries; j++) {
124: if (j != 0) {
125: Thread.sleep(assertionRetryTimeout);
126: }
127: EventQueue.invokeAndWait(rm);
128: result = (Boolean) rm.getResult();
129: if (result == null || result.booleanValue()) {
130: break;
131: }
132: }
133: if (result == null || !result.booleanValue()) {
134: return false;
135: }
136: } else if (m.getReturnType() == void.class) { // assertion
137: EventQueue.invokeAndWait(rm);
138: }
139: }
140: return true;
141: } catch (Throwable e) {
142: e.printStackTrace();
143: return false;
144: }
145: }
146:
147: public static synchronized boolean run(final Object o,
148: final String[] sequence) {
149: setTestCase(o);
150: return run(sequence);
151: }
152: }
153:
154: protected static Robot robot;
155:
156: public RobotTestCase() {
157: if (robot == null) {
158: try {
159: robot = new Robot();
160: } catch (AWTException e) {
161: e.printStackTrace();
162: }
163: }
164: }
165:
166: @Override
167: public void setUp() throws Exception {
168: super .setUp();
169: RobotRunner.setTestCase(this );
170: }
171:
172: public void runScenario(final String[] sequence) {
173: assertTrue(RobotRunner.run(sequence));
174: }
175:
176: public static void setAssertionRetryTimeout(final int timeout) {
177: RobotRunner.assertionRetryTimeout = timeout;
178: }
179:
180: public static void setNumberOfAssertionRetries(final int numRetries) {
181: RobotRunner.numAssertionRetries = numRetries;
182: }
183: }
|