001 /*
002 * Copyright 2006 Google Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package com.google.gwt.junit.client;
017
018 import com.google.gwt.core.ext.Generator;
019 import com.google.gwt.junit.rebind.JUnitTestCaseStubGenerator;
020
021 import junit.framework.TestCase;
022 import junit.framework.TestResult;
023
024 import java.lang.reflect.InvocationTargetException;
025 import java.lang.reflect.Method;
026 import java.util.ArrayList;
027
028 /**
029 * Acts as a bridge between the JUnit environment and the GWT environment. We
030 * hook the run method and stash the TestResult object for later communication
031 * between the test runner and the unit test shell that drives the test case
032 * inside a hosted browser.
033 *
034 * <p>
035 * There are two versions of this class. One is used in hosted mode and the
036 * other is used in hybrid mode. Implementations are very different between the
037 * two modes, making it simpler to have a separate implementation for each.
038 * Please see the <code>translatable</code> subpackage for the other
039 * implementation.
040 * </p>
041 */
042 public abstract class GWTTestCase extends TestCase {
043
044 private static Method runTestMethod;
045 private static Object unitTestShell;
046
047 /**
048 * Accumulates messages that are printed along with failures (useful in hybrid
049 * mode as a substitute for stack traces).
050 */
051 public void addCheckpoint(String msg) {
052 checkPointMessages.add(msg);
053 }
054
055 /**
056 * Determines whether or not exceptions will be caught by the test fixture.
057 */
058 public boolean catchExceptions() {
059 return true;
060 }
061
062 /**
063 * Resets the current checkpoint messages.
064 */
065 public void clearCheckpoints() {
066 checkPointMessages.clear();
067 }
068
069 /**
070 * Gets the current checkpoint messages.
071 */
072 public String[] getCheckpoints() {
073 return (String[]) checkPointMessages.toArray(new String[checkPointMessages
074 .size()]);
075 }
076
077 /**
078 * Tests can only be run in the context of a module. Therefore, the concrete
079 * TestCase must provide an implementation for this method.
080 *
081 * @return the fully qualified name of the module to use for this test.
082 */
083 public abstract String getModuleName();
084
085 /**
086 * Stashes the <code>TestResult</code> object so that it can be accessed
087 * from the unit test shell.
088 */
089 public void run(TestResult result) {
090 testResult = result;
091 super.run(result);
092 }
093
094 /**
095 * Runs the test by delegating to the unit test shell.
096 */
097 protected void runTest() throws Throwable {
098 Throwable caught;
099 try {
100 if (runTestMethod == null) {
101 // Use reflection to avoid build-time dependencies and to provide an
102 // opportunity for a useful failure message.
103 //
104 Class unitTestShellClass;
105 try {
106 unitTestShellClass = Class
107 .forName("com.google.gwt.dev.GWTUnitTestShell");
108 Method getUnitTestShellMethod = unitTestShellClass.getDeclaredMethod(
109 "getUnitTestShell", new Class[]{Generator.class});
110 runTestMethod = unitTestShellClass.getDeclaredMethod("runTest",
111 new Class[]{String.class, TestCase.class, TestResult.class});
112 Generator generator = new JUnitTestCaseStubGenerator(getClass()
113 .getName());
114 unitTestShell = getUnitTestShellMethod.invoke(null,
115 new Object[]{generator});
116
117 if (unitTestShell == null) {
118 throw new RuntimeException(
119 "Unable to start required development shell support; see the console for details");
120 }
121 } catch (ClassNotFoundException e) {
122 throw new RuntimeException(
123 "Unable to find JUnit integration support; are you running with the proper dev jar?");
124 }
125 }
126
127 runTestMethod.invoke(unitTestShell, new Object[]{
128 getModuleName(), this, testResult});
129
130 clearCheckpoints();
131
132 return;
133
134 } catch (SecurityException e) {
135 caught = e;
136 } catch (NoSuchMethodException e) {
137 caught = e;
138 } catch (IllegalArgumentException e) {
139 caught = e;
140 } catch (IllegalAccessException e) {
141 caught = e;
142 } catch (InvocationTargetException e) {
143 caught = e;
144 Throwable nested = caught.getCause();
145 if (nested != null) {
146 caught = nested;
147 }
148 }
149 throw new RuntimeException(
150 "Unable to run unit test due to an unexpected exception", caught);
151 }
152
153 /**
154 * Not supported in the current implemenation.
155 */
156 protected final void setUp() throws Exception {
157 }
158
159 /**
160 * Not supported in the current implementation.
161 */
162 protected void tearDown() throws Exception {
163 }
164
165 /*
166 * A list of messages that serve as debugging aids to track down hybrid mode
167 * failures.
168 */
169 private ArrayList checkPointMessages = new ArrayList();
170
171 /*
172 * Object that collects the results of this test case execution.
173 */
174 private TestResult testResult = null;
175 }