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: */
005: package com.tctest;
006:
007: import EDU.oswego.cs.dl.util.concurrent.CyclicBarrier;
008:
009: import com.tc.object.config.ConfigVisitor;
010: import com.tc.object.config.DSOClientConfigHelper;
011: import com.tc.object.config.TransparencyClassSpec;
012: import com.tc.object.config.spec.CyclicBarrierSpec;
013: import com.tc.simulator.app.ApplicationConfig;
014: import com.tc.simulator.listener.ListenerProvider;
015: import com.tctest.runner.AbstractErrorCatchingTransparentApp;
016:
017: import java.lang.reflect.InvocationTargetException;
018: import java.lang.reflect.Method;
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025:
026: public abstract class GenericTestApp extends
027: AbstractErrorCatchingTransparentApp {
028:
029: private static final String METHOD_PREFIX = "test";
030: private static final String METHOD_PATTERN = "^" + METHOD_PREFIX
031: + ".*$";
032:
033: // roots
034: private final CyclicBarrier barrier;
035: private final CyclicBarrier barrier2;
036: private final Exit exit = new Exit();
037: protected final Map sharedMap = new HashMap();
038:
039: private final Class type;
040: private final List tests;
041: private final int variants;
042:
043: private transient boolean mutator;
044:
045: public GenericTestApp(String appId, ApplicationConfig cfg,
046: ListenerProvider listenerProvider, Class type) {
047: this (appId, cfg, listenerProvider, type, 1);
048: }
049:
050: public GenericTestApp(String appId, ApplicationConfig cfg,
051: ListenerProvider listenerProvider, Class type, int variants) {
052: super (appId, cfg, listenerProvider);
053:
054: final int count = getParticipantCount();
055: if (count < 2) {
056: throw new RuntimeException("wrong number of nodes: "
057: + count);
058: }
059:
060: this .barrier = new CyclicBarrier(getParticipantCount());
061: this .barrier2 = new CyclicBarrier(getParticipantCount());
062:
063: this .type = type;
064: this .variants = variants;
065: this .tests = getTestNames();
066: }
067:
068: protected abstract Object getTestObject(String testName);
069:
070: protected abstract void setupTestObject(String testName);
071:
072: private void makeTestObject(String test) {
073: synchronized (sharedMap) {
074: sharedMap.clear(); // don't want any cross talk
075: setupTestObject(test);
076: }
077: }
078:
079: public void runTest() throws Throwable {
080: int num = barrier.barrier();
081: mutator = (num == 0);
082:
083: if (mutator) {
084: doMutate();
085: } else {
086: doValidate();
087: }
088: }
089:
090: protected boolean isMutator() {
091: return mutator;
092: }
093:
094: private void doValidate() throws Throwable {
095: Thread.currentThread().setName(
096: "VALIDATOR " + getApplicationId());
097: for (Iterator i = tests.iterator(); i.hasNext();) {
098: String name = (String) i.next();
099:
100: for (int variant = 1; variant <= variants; variant++) {
101: barrier.barrier();
102:
103: if (exit.shouldExit()) {
104: return;
105: }
106:
107: try {
108: runOp(name, true, variant);
109: } catch (Throwable t) {
110: exit.toggle();
111: throw t;
112: } finally {
113: barrier2.barrier();
114: }
115:
116: if (exit.shouldExit()) {
117: return;
118: }
119: }
120: }
121: }
122:
123: private void doMutate() throws Throwable {
124: Thread.currentThread().setName("MUTATOR " + getApplicationId());
125: for (Iterator i = tests.iterator(); i.hasNext();) {
126: String name = (String) i.next();
127:
128: System.err.print("Running test: " + name + " ... ");
129: long start = System.currentTimeMillis();
130:
131: for (int variant = 1; variant <= variants; variant++) {
132: try {
133: runOp(name, false, variant);
134: runOp(name, true, variant);
135: } catch (Throwable t) {
136: exit.toggle();
137: throw t;
138: } finally {
139: barrier.barrier();
140:
141: if (!exit.shouldExit()) {
142: barrier2.barrier();
143: }
144: }
145:
146: if (exit.shouldExit()) {
147: return;
148: }
149: }
150:
151: System.err.println(" took "
152: + (System.currentTimeMillis() - start) + " millis");
153: }
154: }
155:
156: private void runOp(String op, boolean validate, int variant)
157: throws Throwable {
158: Method m = findMethod(op);
159:
160: if (!validate) {
161: makeTestObject(op);
162: }
163:
164: Object object = getTestObject(op);
165:
166: if (object instanceof Iterator) {
167: // do some automagic for Iterators
168: for (Iterator i = (Iterator) object; i.hasNext();) {
169: runMethod(m, i.next(), validate, variant);
170: }
171: } else {
172: runMethod(m, object, validate, variant);
173: }
174: }
175:
176: private void runMethod(Method m, Object object, boolean validate,
177: int variant) throws Throwable {
178: final Object[] args;
179: if (variants > 1) {
180: args = new Object[] { object, Boolean.valueOf(validate),
181: new Integer(variant) };
182: } else {
183: args = new Object[] { object, Boolean.valueOf(validate) };
184: }
185:
186: try {
187: m.invoke(this , args);
188: } catch (InvocationTargetException ite) {
189: throw ite.getTargetException();
190: }
191: }
192:
193: private Method findMethod(String name) throws NoSuchMethodException {
194: final Class[] sig;
195: if (variants > 1) {
196: sig = new Class[] { type, Boolean.TYPE, Integer.TYPE };
197: } else {
198: sig = new Class[] { type, Boolean.TYPE };
199: }
200:
201: Method method = getClass().getDeclaredMethod(
202: METHOD_PREFIX + name, sig);
203: method.setAccessible(true);
204: return method;
205: }
206:
207: private List getTestNames() {
208: List rv = new ArrayList();
209: Class klass = getClass();
210: Method[] methods = klass.getDeclaredMethods();
211: for (int i = 0; i < methods.length; i++) {
212: Method m = methods[i];
213: if (m.getName().matches(METHOD_PATTERN)) {
214: Class[] args = m.getParameterTypes();
215:
216: final boolean ok;
217: if (variants > 1) {
218: ok = (args.length == 3) && args[0].equals(type)
219: && args[1].equals(Boolean.TYPE)
220: && args[2].equals(Integer.TYPE);
221: } else {
222: ok = (args.length == 2) && args[0].equals(type)
223: && args[1].equals(Boolean.TYPE);
224: }
225:
226: if (ok) {
227: rv.add(m.getName().replaceFirst(METHOD_PREFIX, ""));
228: } else {
229: throw new RuntimeException("bad method: " + m);
230: }
231: }
232: }
233:
234: if (rv.size() <= 0) {
235: throw new RuntimeException("Didn't find any operations");
236: }
237:
238: // make test order predictable (although this is a bad thing to rely on)
239: Collections.sort(rv);
240:
241: return rv;
242: }
243:
244: public static void visitL1DSOConfig(ConfigVisitor visitor,
245: DSOClientConfigHelper config) {
246: String testClass = GenericTestApp.class.getName();
247: TransparencyClassSpec spec = config.getOrCreateSpec(testClass);
248: config.getOrCreateSpec(Exit.class.getName());
249:
250: spec.addRoot("sharedMap", "sharedMap");
251: spec.addRoot("barrier", "barrier");
252: spec.addRoot("barrier2", "barrier2");
253: spec.addRoot("exit", "exit");
254:
255: String methodExpression = "* " + testClass + "*.*(..)";
256: config.addWriteAutolock(methodExpression);
257:
258: new CyclicBarrierSpec().visit(visitor, config);
259: }
260:
261: private static class Exit {
262: private boolean exit = false;
263:
264: synchronized boolean shouldExit() {
265: return exit;
266: }
267:
268: synchronized void toggle() {
269: exit = true;
270: }
271: }
272:
273: }
|