1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package net.grinder.scriptengine.jython.instrumentation;
23
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertSame;
28 import static org.junit.Assert.fail;
29
30 import java.lang.reflect.Field;
31
32 import net.grinder.common.StubTest;
33 import net.grinder.common.UncheckedGrinderException;
34 import net.grinder.common.UncheckedInterruptedException;
35 import net.grinder.script.NonInstrumentableTypeException;
36 import net.grinder.script.NotWrappableTypeException;
37 import net.grinder.script.Test.InstrumentationFilter;
38 import net.grinder.scriptengine.Instrumenter;
39 import net.grinder.scriptengine.Recorder;
40 import net.grinder.testutility.AssertUtilities;
41 import net.grinder.testutility.RandomStubFactory;
42
43 import org.junit.Test;
44 import org.python.core.PyClass;
45 import org.python.core.PyException;
46 import org.python.core.PyInstance;
47 import org.python.core.PyInteger;
48 import org.python.core.PyObject;
49 import org.python.core.PyProxy;
50 import org.python.core.PySystemState;
51 import org.python.core.PyTuple;
52 import org.python.util.PythonInterpreter;
53
54
55
56
57
58
59
60 public abstract class AbstractJythonInstrumenterTestCase {
61
62 {
63 PySystemState.initialize();
64 }
65
66 protected final Instrumenter m_instrumenter;
67
68 protected final PythonInterpreter m_interpreter =
69 new PythonInterpreter(null, new PySystemState());
70
71 protected final PyObject m_zero = new PyInteger(0);
72 protected final PyObject m_one = new PyInteger(1);
73 protected final PyObject m_two = new PyInteger(2);
74 protected final PyObject m_three = new PyInteger(3);
75 protected final PyObject m_six = new PyInteger(6);
76 protected final net.grinder.common.Test m_test = new StubTest(1, "test");
77
78 protected final RandomStubFactory<Recorder> m_recorderStubFactory =
79 RandomStubFactory.create(Recorder.class);
80 protected final Recorder m_recorder = m_recorderStubFactory.getStub();
81
82 public AbstractJythonInstrumenterTestCase(Instrumenter instrumenter) {
83 super();
84 m_instrumenter = instrumenter;
85 }
86
87 protected abstract void assertTestReference(PyObject proxy,
88 net.grinder.common.Test test);
89
90 protected abstract void assertTargetReference(PyObject proxy,
91 Object original,
92 boolean unwrapTarget);
93
94 protected void assertTargetReference(PyObject proxy, Object original) {
95 assertTargetReference(proxy, original, false);
96 }
97
98 public static Integer[] getJythonVersion() throws Exception {
99 final PyTuple pyTuple =
100 (PyTuple) PySystemState.class.getField("version_info").get(null);
101
102 return new Integer[] { getVersionPart(pyTuple, 0),
103 getVersionPart(pyTuple, 1),
104 getVersionPart(pyTuple, 2), };
105 }
106
107 private static Integer getVersionPart(PyTuple versionTuple, int part) {
108
109 return (Integer)versionTuple.__finditem__(part).__tojava__(Integer.class);
110 }
111
112 public static void assertVersion(String expected) throws Exception {
113 AssertUtilities.assertContainsPattern(
114 PySystemState.class.getField("version").get(null).toString(),
115 expected);
116 }
117
118 protected final PythonInterpreter getInterpretter() {
119 return m_interpreter;
120 }
121
122 protected final void assertNotWrappable(Object o) throws Exception {
123 try {
124 m_instrumenter.createInstrumentedProxy(null, null, o);
125 fail("Expected NotWrappableTypeException");
126 }
127 catch (NotWrappableTypeException e) {
128 }
129 }
130
131 protected final void assertNotWrappableByThisInstrumenter(Object o)
132 throws Exception {
133 assertNull(m_instrumenter.createInstrumentedProxy(null, null, o));
134 }
135
136 protected final Object createInstrumentedProxy(net.grinder.common.Test test,
137 Recorder recorder,
138 PyObject pyTarget)
139 throws NotWrappableTypeException {
140
141
142
143 final Object javaTarget = pyTarget.__tojava__(Object.class);
144
145 return m_instrumenter.createInstrumentedProxy(test, recorder, javaTarget);
146 }
147
148 protected final Class<?> getClassForInstance(PyInstance target)
149 throws IllegalArgumentException, IllegalAccessException {
150
151 Field f;
152
153 try {
154
155 f = PyObject.class.getField("__class__");
156 }
157 catch (NoSuchFieldException e) {
158
159 try {
160 f = PyInstance.class.getField("instclass");
161 }
162 catch (NoSuchFieldException e2) {
163 throw new AssertionError("Incompatible Jython release in classpath");
164 }
165 }
166
167 final PyClass pyClass = (PyClass)f.get(target);
168
169 return (Class<?>) pyClass.__tojava__(Class.class);
170 }
171
172 @Test public void testCreateProxyWithPyFunction() throws Exception {
173 m_interpreter.exec("def return1(): return 1");
174 final PyObject pyFunction = m_interpreter.get("return1");
175 final PyObject pyFunctionProxy = (PyObject)
176 createInstrumentedProxy(m_test, m_recorder, pyFunction);
177
178 final PyObject result = pyFunctionProxy.invoke("__call__");
179 assertEquals(m_one, result);
180 m_recorderStubFactory.assertSuccess("start");
181 m_recorderStubFactory.assertSuccess("end", true);
182 m_recorderStubFactory.assertNoMoreCalls();
183 assertTestReference(pyFunctionProxy, m_test);
184 assertTargetReference(pyFunctionProxy, pyFunction);
185
186 m_interpreter.exec("def multiply(x, y): return x * y");
187 final PyObject pyFunction2 = m_interpreter.get("multiply");
188 final PyObject pyFunctionProxy2 = (PyObject)
189 createInstrumentedProxy(m_test, m_recorder, pyFunction2);
190 final PyObject result2 =
191 pyFunctionProxy2.invoke("__call__", m_two, m_three);
192 assertEquals(m_six, result2);
193 m_recorderStubFactory.assertSuccess("start");
194 m_recorderStubFactory.assertSuccess("end", true);
195 m_recorderStubFactory.assertNoMoreCalls();
196 assertTargetReference(pyFunctionProxy2, pyFunction2);
197
198 final PyObject result3 =
199 pyFunctionProxy2.invoke("__call__", new PyObject[] { m_two, m_three});
200 assertEquals(m_six, result3);
201 m_recorderStubFactory.assertSuccess("start");
202 m_recorderStubFactory.assertSuccess("end", true);
203 m_recorderStubFactory.assertNoMoreCalls();
204
205 m_interpreter.exec("def square(x): return x * x");
206 final PyObject pyFunction11 = m_interpreter.get("square");
207 final PyObject pyFunctionProxy11 = (PyObject)
208 createInstrumentedProxy(m_test, m_recorder, pyFunction11);
209 final PyObject result11 = pyFunctionProxy11.invoke("__call__", m_two);
210 assertEquals(new PyInteger(4), result11);
211 m_recorderStubFactory.assertSuccess("start");
212 m_recorderStubFactory.assertSuccess("end", true);
213 m_recorderStubFactory.assertNoMoreCalls();
214 assertTargetReference(pyFunctionProxy11, pyFunction11);
215
216
217 m_interpreter.set("proxy", pyFunctionProxy);
218 m_interpreter.set("proxy2", pyFunctionProxy2);
219
220 m_interpreter.exec("result5 = proxy()");
221 final PyObject result5 = m_interpreter.get("result5");
222 assertEquals(m_one, result5);
223 m_recorderStubFactory.assertSuccess("start");
224 m_recorderStubFactory.assertSuccess("end", true);
225 m_recorderStubFactory.assertNoMoreCalls();
226 }
227
228 @Test public void testCreateProxyWithPyInstance() throws Exception {
229
230 m_interpreter.exec(
231 "class Foo:\n" +
232 " def two(self): return 2\n" +
233 " def identity(self, x): return x\n" +
234 " def sum(self, x, y): return x + y\n" +
235 " def sum3(self, x, y, z): return x + y + z\n" +
236 "x=Foo()");
237
238 final PyObject pyInstance = m_interpreter.get("x");
239 final PyObject pyInstanceProxy = (PyObject)
240 createInstrumentedProxy(m_test, m_recorder, pyInstance);
241 final PyObject result1 = pyInstanceProxy.invoke("two");
242 assertEquals(m_two, result1);
243 m_recorderStubFactory.assertSuccess("start");
244 m_recorderStubFactory.assertSuccess("end", true);
245 m_recorderStubFactory.assertNoMoreCalls();
246 assertTestReference(pyInstanceProxy, m_test);
247 assertNull(pyInstanceProxy.__findattr__("__blah__"));
248 assertTargetReference(pyInstanceProxy, pyInstance);
249
250 final PyObject result2 = pyInstanceProxy.invoke("identity", m_one);
251 assertSame(m_one, result2);
252 m_recorderStubFactory.assertSuccess("start");
253 m_recorderStubFactory.assertSuccess("end", true);
254 m_recorderStubFactory.assertNoMoreCalls();
255
256 final PyObject result3 = pyInstanceProxy.invoke("sum", m_one, m_two);
257 assertEquals(m_three, result3);
258 m_recorderStubFactory.assertSuccess("start");
259 m_recorderStubFactory.assertSuccess("end", true);
260 m_recorderStubFactory.assertNoMoreCalls();
261
262 final PyObject result4 = pyInstanceProxy.invoke("sum3", new PyObject[] {
263 m_one, m_two, m_three });
264 assertEquals(m_six, result4);
265 m_recorderStubFactory.assertSuccess("start");
266 m_recorderStubFactory.assertSuccess("end", true);
267 m_recorderStubFactory.assertNoMoreCalls();
268
269 final PyObject result5 = pyInstanceProxy.invoke("sum", new PyObject[] {
270 m_one, m_two }, new String[] { "x", "y" });
271 assertEquals(m_three, result5);
272 m_recorderStubFactory.assertSuccess("start");
273 m_recorderStubFactory.assertSuccess("end", true);
274 m_recorderStubFactory.assertNoMoreCalls();
275
276
277 m_interpreter.set("proxy", pyInstanceProxy);
278
279 m_interpreter.exec("result6 = proxy.sum(2, 4)");
280 final PyObject result6 = m_interpreter.get("result6");
281 assertEquals(m_six, result6);
282 m_recorderStubFactory.assertSuccess("start");
283 m_recorderStubFactory.assertSuccess("end", true);
284 m_recorderStubFactory.assertNoMoreCalls();
285 }
286
287 @Test public void testCreateProxyWithUnboundPyMethod() throws Exception {
288 m_interpreter.exec(
289 "class Foo:\n" +
290 " def two(self): return 2\n" +
291 " def identity(self, x): return x\n" +
292 " def sum(self, x, y): return x + y\n" +
293 " def sum3(self, x, y, z): return x + y + z\n" +
294 "x=Foo()");
295 final PyObject pyInstance = m_interpreter.get("x");
296 m_interpreter.exec("y=Foo.two");
297 final PyObject pyMethod = m_interpreter.get("y");
298 final PyObject pyMethodProxy = (PyObject)
299 createInstrumentedProxy(m_test, m_recorder, pyMethod);
300 final PyObject result = pyMethodProxy.invoke("__call__", pyInstance);
301 assertEquals(m_two, result);
302 m_recorderStubFactory.assertSuccess("start");
303 m_recorderStubFactory.assertSuccess("end", true);
304 m_recorderStubFactory.assertNoMoreCalls();
305 assertTestReference(pyMethodProxy, m_test);
306 assertNull(pyMethodProxy.__findattr__("__blah__"));
307 assertTargetReference(pyMethodProxy, pyMethod);
308
309 m_interpreter.exec("y=Foo.identity");
310 final PyObject pyMethod2 = m_interpreter.get("y");
311 final PyObject pyMethodProxy2 = (PyObject)
312 createInstrumentedProxy(m_test, m_recorder, pyMethod2);
313 final PyObject result2 =
314 pyMethodProxy2.invoke("__call__", pyInstance, m_one);
315 assertEquals(m_one, result2);
316 m_recorderStubFactory.assertSuccess("start");
317 m_recorderStubFactory.assertSuccess("end", true);
318 m_recorderStubFactory.assertNoMoreCalls();
319
320 m_interpreter.exec("y=Foo.sum");
321 final PyObject pyMethod3 = m_interpreter.get("y");
322 final PyObject pyMethodProxy3 = (PyObject)
323 createInstrumentedProxy(m_test, m_recorder, pyMethod3);
324 final PyObject result3 =
325 pyMethodProxy3.invoke(
326 "__call__", new PyObject[] { pyInstance, m_one, m_two });
327 assertEquals(m_three, result3);
328 m_recorderStubFactory.assertSuccess("start");
329 m_recorderStubFactory.assertSuccess("end", true);
330 m_recorderStubFactory.assertNoMoreCalls();
331
332
333 m_interpreter.set("proxy", pyMethodProxy);
334
335 m_interpreter.exec("result5 = proxy(x)");
336 final PyObject result5 = m_interpreter.get("result5");
337 assertEquals(m_two, result5);
338 m_recorderStubFactory.assertSuccess("start");
339 m_recorderStubFactory.assertSuccess("end", true);
340 m_recorderStubFactory.assertNoMoreCalls();
341 }
342
343 @Test public void testCreateProxyWithBoundPyMethod() throws Exception {
344 m_interpreter.exec(
345 "class Foo:\n" +
346 " def two(self): return 2\n" +
347 "x=Foo()\n" +
348 "y=Foo()\n");
349
350 m_interpreter.exec("z=x.two");
351 final PyObject pyMethod = m_interpreter.get("z");
352 final PyObject pyMethodProxy = (PyObject)
353 createInstrumentedProxy(m_test, m_recorder, pyMethod);
354 final PyObject result = pyMethodProxy.invoke("__call__");
355 assertEquals(m_two, result);
356 m_recorderStubFactory.assertSuccess("start");
357 m_recorderStubFactory.assertSuccess("end", true);
358
359
360 m_interpreter.exec("z=y.two");
361 final PyObject pyMethod2 = m_interpreter.get("z");
362 final PyObject result2 = pyMethod2.invoke("__call__");
363 assertEquals(m_two, result2);
364
365 m_recorderStubFactory.assertNoMoreCalls();
366 }
367
368 @Test public void testCreateProxyWithPyReflectedFunction() throws Exception {
369 m_interpreter.exec("from grinder.test import MyClass\nx=MyClass(6, 5, 4)");
370 final PyObject pyJava = m_interpreter.get("x");
371 m_interpreter.exec("y=MyClass.getA");
372 final PyObject pyJavaMethod = m_interpreter.get("y");
373 final PyObject pyJavaMethodProxy = (PyObject)
374 createInstrumentedProxy(m_test, m_recorder, pyJavaMethod);
375 final PyObject result = pyJavaMethodProxy.__call__(pyJava);
376 assertEquals(m_six, result);
377 m_recorderStubFactory.assertSuccess("start");
378 m_recorderStubFactory.assertSuccess("end", true);
379 m_recorderStubFactory.assertNoMoreCalls();
380 assertTestReference(pyJavaMethodProxy, m_test);
381 assertNull(pyJavaMethodProxy.__findattr__("__blah__"));
382 assertTargetReference(pyJavaMethodProxy, pyJavaMethod);
383
384
385 m_interpreter.set("proxy", pyJavaMethodProxy);
386
387 m_interpreter.exec("result2 = proxy(x)");
388 final PyObject result2 = m_interpreter.get("result2");
389 assertEquals(m_six, result2);
390 m_recorderStubFactory.assertSuccess("start");
391 m_recorderStubFactory.assertSuccess("end", true);
392 m_recorderStubFactory.assertNoMoreCalls();
393 }
394
395 private PyObject getPyInstance(PyProxy pyProxy) throws Exception {
396
397 return (PyObject) PyProxy.class.getMethod("_getPyInstance").invoke(pyProxy);
398 }
399
400 @Test public void testCreateProxyWithPyProxy() throws Exception {
401 m_interpreter.exec("from java.util import Random");
402 m_interpreter.exec(
403 "class PyRandom(Random):\n" +
404 " def one(self): return 1\n" +
405 "x=PyRandom()");
406 final PyObject pyInstance = m_interpreter.get("x");
407
408
409
410 final PyProxy pyProxy = (PyProxy) pyInstance.__tojava__(PyProxy.class);
411 final Object pyProxyProxy =
412 m_instrumenter.createInstrumentedProxy(m_test,
413 m_recorder,
414 pyProxy);
415
416 final PyObject pyProxyInstance =
417 (pyProxyProxy instanceof PyProxy) ?
418 getPyInstance((PyProxy) pyProxyProxy) : (PyObject)pyProxyProxy;
419
420 final PyObject result = pyProxyInstance.invoke("one");
421 assertEquals(m_one, result);
422 m_recorderStubFactory.assertSuccess("start");
423 m_recorderStubFactory.assertSuccess("end", true);
424 m_recorderStubFactory.assertNoMoreCalls();
425 assertTestReference(pyProxyInstance, m_test);
426 assertTargetReference(pyProxyInstance, pyInstance);
427
428
429 m_interpreter.set("proxy", pyProxyProxy);
430
431 m_interpreter.exec("result2 = proxy.one()");
432 final PyObject result2 = m_interpreter.get("result2");
433 assertEquals(m_one, result2);
434 m_recorderStubFactory.assertSuccess("start");
435 m_recorderStubFactory.assertSuccess("end", true);
436 m_recorderStubFactory.assertNoMoreCalls();
437
438 m_interpreter.exec("result3 = proxy.nextInt()");
439 final PyObject result3 = m_interpreter.get("result3");
440 assertNotNull(result3);
441 m_recorderStubFactory.assertSuccess("start");
442 m_recorderStubFactory.assertSuccess("end", true);
443 m_recorderStubFactory.assertNoMoreCalls();
444 }
445
446 @Test public void testCreateProxyWithRecursiveCode() throws Exception {
447 m_interpreter.exec(
448 "class Recurse:\n" +
449 " def __init__(self):\n" +
450 " self.i = 3\n" +
451 " def foo(self):\n" +
452 " self.i = self.i - 1\n" +
453 " if self.i == 0: return 0\n" +
454 " return self.i + self.foo()\n" +
455 "r = Recurse()");
456
457 final PyObject proxy = (PyObject)
458 createInstrumentedProxy(m_test, m_recorder, m_interpreter.get("r"));
459
460 final PyObject result = proxy.invoke("foo");
461
462 assertEquals(new PyInteger(3), result);
463
464
465 m_recorderStubFactory.assertSuccess("start");
466 m_recorderStubFactory.assertSuccess("start");
467 m_recorderStubFactory.assertSuccess("start");
468 m_recorderStubFactory.assertSuccess("end", true);
469 m_recorderStubFactory.assertSuccess("end", true);
470 m_recorderStubFactory.assertSuccess("end", true);
471 m_recorderStubFactory.assertNoMoreCalls();
472 }
473
474 @Test public void testPyDispatcherErrorHandling() throws Exception {
475 m_interpreter.exec("def blah(): raise Exception('a problem')");
476 final PyObject pyFunction = m_interpreter.get("blah");
477 final PyObject pyFunctionProxy = (PyObject)
478 createInstrumentedProxy(m_test, m_recorder, pyFunction);
479 try {
480 pyFunctionProxy.invoke("__call__");
481 fail("Expected PyException");
482 }
483 catch (PyException e) {
484 AssertUtilities.assertContains(e.toString(), "a problem");
485 }
486
487 m_recorderStubFactory.assertSuccess("start");
488 m_recorderStubFactory.assertSuccess("end", false);
489 m_recorderStubFactory.assertNoMoreCalls();
490
491 final UncheckedGrinderException e = new UncheckedInterruptedException(null);
492 m_recorderStubFactory.setThrows("start", e);
493
494 try {
495 pyFunctionProxy.invoke("__call__");
496 fail("Expected UncheckedGrinderException");
497 }
498 catch (UncheckedGrinderException e2) {
499 assertSame(e, e2);
500 }
501 catch (PyException e3) {
502 assertSame(e, e3.value.__tojava__(Exception.class));
503 }
504 }
505
506 @Test public void testSelectiveInstrumentUnsupported() throws Exception {
507 m_interpreter.exec(
508 "class Foo:\n" +
509 " def two(self): return 2\n" +
510 " def identity(self, x): return x\n" +
511 " def sum(self, x, y): return x + y\n" +
512 " def sum3(self, x, y, z): return x + y + z\n" +
513 "x=Foo()");
514
515 final PyObject pyInstance = m_interpreter.get("x");
516
517 final InstrumentationFilter filter = new InstrumentationFilter() {
518 public boolean matches(Object item) {
519 return true;
520 }
521 };
522
523 try {
524 m_instrumenter.instrument(m_test, m_recorder, pyInstance, filter);
525 fail("Expected NonInstrumentableTypeException");
526 }
527 catch (NonInstrumentableTypeException e) {
528 }
529 }
530 }