View Javadoc

1   // Copyright (C) 2009 - 2011 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.scriptengine.jython.instrumentation.dcr;
23  
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertNotSame;
26  import static org.junit.Assert.assertSame;
27  import net.grinder.engine.process.dcr.RecorderLocatorAccess;
28  import net.grinder.scriptengine.Instrumenter;
29  import net.grinder.scriptengine.jython.instrumentation.AbstractJythonInstrumenterTestCase;
30  
31  import org.junit.After;
32  import org.junit.Test;
33  import org.python.core.PyObject;
34  import org.python.util.PythonInterpreter;
35  
36  
37  /**
38   * Common stuff for Jython DCR instrumenters.
39   *
40   * @author Philip Aston
41   */
42  public abstract class AbstractJythonDCRInstrumenterTestCase
43    extends AbstractJythonInstrumenterTestCase {
44  
45    public AbstractJythonDCRInstrumenterTestCase(Instrumenter instrumenter) {
46      super(instrumenter);
47    }
48  
49    @After public void tearDown() throws Exception {
50      RecorderLocatorAccess.clearRecorders();
51    }
52  
53    @Override
54    protected void assertTestReference(PyObject pyObject,
55                                       net.grinder.common.Test test) {
56      // No-op, AbstractDCRInstrumenter doesn't support __test__.
57    }
58  
59    @Override
60    protected void assertTargetReference(PyObject proxy,
61                                         Object original,
62                                         boolean unwrapTarget) {
63      // AbstractDCRInstrumenter doesn't support __target__.
64    }
65  
66    @Test public void testInstrumentationWithNonWrappableParameters()
67      throws Exception {
68  
69      // The types that can be wrapped depend on the Instrumenter.
70  
71      final PythonInterpreter interpreter = getInterpretter();
72  
73      // Can't wrap PyInteger.
74      interpreter.exec("x=1");
75      assertNotWrappable(interpreter.get("x"));
76  
77      assertNotWrappableByThisInstrumenter(null);
78  
79      // assertNotWrappableByThisInstrumenter(MyClass.class);
80    }
81  
82    @Test public void testInstrumentationWithPyClass() throws Exception {
83      m_interpreter.exec("class Foo:\n" +
84                         " def __init__(self, a, b, c):\n" +
85                         "  self.a = a\n" +
86                         "  self.b = b\n" +
87                         "  self.c = c\n" +
88                         " def six(self): return 6\n");
89  
90      final PyObject pyType = m_interpreter.get("Foo");
91      createInstrumentedProxy(m_test, m_recorder, pyType);
92      final PyObject result = pyType.__call__(m_two, m_three, m_one);
93      assertEquals(m_two, result.__getattr__("a"));
94      m_recorderStubFactory.assertSuccess("start");
95      m_recorderStubFactory.assertSuccess("end", true);
96      m_recorderStubFactory.assertNoMoreCalls();
97  
98      // From Jython.
99      m_interpreter.set("proxy", pyType);
100 
101     m_interpreter.exec("result2 = Foo(1, 2, 3)");
102     final PyObject result2 = m_interpreter.get("result2");
103     assertEquals(m_two, result2.__getattr__("b"));
104     m_recorderStubFactory.assertSuccess("start");
105     m_recorderStubFactory.assertSuccess("end", true);
106     m_recorderStubFactory.assertNoMoreCalls();
107 
108     m_interpreter.exec("result3 = proxy(0, 0, 0)");
109     final PyObject result3 = m_interpreter.get("result3");
110     assertEquals(m_zero, result3.__getattr__("b"));
111     m_recorderStubFactory.assertSuccess("start");
112     m_recorderStubFactory.assertSuccess("end", true);
113     m_recorderStubFactory.assertNoMoreCalls();
114 
115     // Instrumenting a class doesn't instrument methods.
116     m_interpreter.exec("result4 = result3.six()");
117     assertEquals(m_six, m_interpreter.get("result4"));
118     m_recorderStubFactory.assertNoMoreCalls();
119   }
120 
121   @Test public void testInstrumentationWithPyDerivedClass() throws Exception {
122     m_interpreter.exec("from grinder.test import MyClass\n" +
123                        "class Foo(MyClass):\n" +
124                        " def six(self): return 6\n" +
125                        "x=Foo()");
126 
127     final PyObject pyType = m_interpreter.get("Foo");
128     createInstrumentedProxy(m_test, m_recorder, pyType);
129     final PyObject result = pyType.__call__();
130     assertEquals(m_zero, result.invoke("getA"));
131     m_recorderStubFactory.assertSuccess("start");
132     m_recorderStubFactory.assertSuccess("end", true);
133     m_recorderStubFactory.assertNoMoreCalls();
134 
135     // From Jython.
136     m_interpreter.set("proxy", pyType);
137 
138     m_interpreter.exec("result2 = Foo()");
139     final PyObject result2 = m_interpreter.get("result2");
140     assertEquals(m_zero, result2.invoke("getB"));
141     m_recorderStubFactory.assertSuccess("start");
142     m_recorderStubFactory.assertSuccess("end", true);
143     m_recorderStubFactory.assertNoMoreCalls();
144 
145     m_interpreter.exec("result3 = proxy(0, 0, 0)");
146     final PyObject result3 = m_interpreter.get("result3");
147     assertEquals(m_zero, result3.invoke("getB"));
148     m_recorderStubFactory.assertSuccess("start");
149     m_recorderStubFactory.assertSuccess("end", true);
150     m_recorderStubFactory.assertNoMoreCalls();
151 
152     // Instrumenting a class doesn't instrument methods.
153     m_interpreter.exec("result4 = result3.six()");
154     assertEquals(m_six, m_interpreter.get("result4"));
155     m_recorderStubFactory.assertNoMoreCalls();
156   }
157 
158   @Test public void testInstrumentationWithStaticMethod() throws Exception {
159     m_interpreter.exec("from grinder.test import MyClass\n" +
160                        "x=MyClass.staticSix");
161 
162     final PyObject pyType = m_interpreter.get("x");
163     createInstrumentedProxy(m_test, m_recorder, pyType);
164     final PyObject result = pyType.__call__();
165     assertEquals(m_six, result);
166     m_recorderStubFactory.assertSuccess("start");
167     m_recorderStubFactory.assertSuccess("end", true);
168     m_recorderStubFactory.assertNoMoreCalls();
169 
170     // From Jython.
171     m_interpreter.set("proxy", pyType);
172 
173     // From Jython 2.5.2, static method references are bound to distinct Jython
174     // instances.
175     m_interpreter.exec("result2 = x() # MyClass.staticSix()");
176     final PyObject result2 = m_interpreter.get("result2");
177     assertEquals(m_six, result2);
178     m_recorderStubFactory.assertSuccess("start");
179     m_recorderStubFactory.assertSuccess("end", true);
180     m_recorderStubFactory.assertNoMoreCalls();
181   }
182 
183   @Test public void testInstrumentationWithReflectedConstructor() throws Exception {
184     m_interpreter.exec("from grinder.test import MyClass\n" +
185                        "x=MyClass.__init__");
186 
187     final PyObject myClass = m_interpreter.get("MyClass");
188     final PyObject py = m_interpreter.get("x");
189     createInstrumentedProxy(m_test, m_recorder, py);
190     myClass.__call__();
191     m_recorderStubFactory.assertSuccess("start");
192     m_recorderStubFactory.assertSuccess("start");
193     m_recorderStubFactory.assertSuccess("end", true);
194     m_recorderStubFactory.assertSuccess("end", true);
195     m_recorderStubFactory.assertNoMoreCalls();
196 
197     // From Jython.
198     m_interpreter.exec("MyClass()");
199     m_recorderStubFactory.assertSuccess("start");
200     m_recorderStubFactory.assertSuccess("start");
201     m_recorderStubFactory.assertSuccess("end", true);
202     m_recorderStubFactory.assertSuccess("end", true);
203     m_recorderStubFactory.assertNoMoreCalls();
204   }
205 
206   @Test public void testInstrumentationWithPyLambda() throws Exception {
207     m_interpreter.exec("f=lambda x:x+1");
208 
209     final PyObject pyType = m_interpreter.get("f");
210     createInstrumentedProxy(m_test, m_recorder, pyType);
211     final PyObject result = pyType.__call__(m_two);
212     assertEquals(m_three, result);
213     m_recorderStubFactory.assertSuccess("start");
214     m_recorderStubFactory.assertSuccess("end", true);
215     m_recorderStubFactory.assertNoMoreCalls();
216 
217     // From Jython.
218     m_interpreter.set("proxy", pyType);
219 
220     m_interpreter.exec("result2 = f(0)");
221     final PyObject result2 = m_interpreter.get("result2");
222     assertEquals(m_one, result2);
223     m_recorderStubFactory.assertSuccess("start");
224     m_recorderStubFactory.assertSuccess("end", true);
225     m_recorderStubFactory.assertNoMoreCalls();
226   }
227 
228   // This doesn't work for DCR.
229   @Test public void testCreateProxyWithPyJavaInstance() throws Exception {
230     m_interpreter.exec("from grinder.test import MyClass\nx=MyClass()");
231     final PyObject pyJava = m_interpreter.get("x");
232     createInstrumentedProxy(m_test, m_recorder, pyJava);
233 
234     final PyObject result = pyJava.invoke("getA");
235     assertEquals(m_zero, result);
236 
237     m_recorderStubFactory.assertSuccess("start");
238     m_recorderStubFactory.assertSuccess("end", true);
239     m_recorderStubFactory.assertNoMoreCalls();
240 
241     // From Jython.
242     m_interpreter.exec("result = x.getB()");
243     final PyObject result2 = m_interpreter.get("result");
244     assertEquals(m_zero, result2);
245 
246     m_recorderStubFactory.assertSuccess("start");
247     m_recorderStubFactory.assertSuccess("end", true);
248     m_recorderStubFactory.assertNoMoreCalls();
249   }
250 
251   @Test public void testCreateProxyWithJavaClass() throws Exception {
252     m_interpreter.exec("from grinder.test import MyClass");
253     final PyObject pyJavaType = m_interpreter.get("MyClass");
254     createInstrumentedProxy(m_test, m_recorder, pyJavaType);
255 
256     m_interpreter.exec("result = MyClass(2, 3, 1)");
257     final PyObject result = m_interpreter.get("result");
258 
259     assertEquals(m_two, result.invoke("getA"));
260     m_recorderStubFactory.assertSuccess("start");
261     m_recorderStubFactory.assertSuccess("end", true);
262     m_recorderStubFactory.assertNoMoreCalls();
263 
264     // From Jython.
265 
266     m_interpreter.exec("result2 = MyClass(1, 2, 3)");
267     final PyObject result2 = m_interpreter.get("result2");
268     assertEquals(m_two, result2.invoke("getB"));
269     m_recorderStubFactory.assertSuccess("start");
270     m_recorderStubFactory.assertSuccess("end", true);
271     m_recorderStubFactory.assertNoMoreCalls();
272 
273     m_interpreter.exec("result3 = MyClass.staticSix()");
274     assertEquals(m_six, m_interpreter.get("result3"));
275     m_recorderStubFactory.assertSuccess("start");
276     m_recorderStubFactory.assertSuccess("end", true);
277     m_recorderStubFactory.assertNoMoreCalls();
278   }
279 
280   /**
281    * See bug 2992248.
282    */
283   @Test public void testJavaBoundMethod() throws Exception {
284     m_interpreter.exec("from grinder.test import MyClass\nx=MyClass(1, 2, 3)");
285 
286     m_interpreter.exec("y=x.getA");
287     final PyObject pyJavaMethod = m_interpreter.get("y");
288     createInstrumentedProxy(m_test, m_recorder, pyJavaMethod);
289 
290     final PyObject result = pyJavaMethod.__call__();
291     assertEquals(m_one, result);
292     m_recorderStubFactory.assertSuccess("start");
293     m_recorderStubFactory.assertSuccess("end", true);
294     m_recorderStubFactory.assertNoMoreCalls();
295 
296     // Test instrumentation works through separate references.
297     m_interpreter.exec("z=x.getA");
298     final PyObject pyJavaMethod2 = m_interpreter.get("z");
299 
300     final PyObject result2 = pyJavaMethod2.__call__();
301     assertNotSame(result, result2);
302 
303     assertEquals(m_one, result);
304     m_recorderStubFactory.assertSuccess("start");
305     m_recorderStubFactory.assertSuccess("end", true);
306     m_recorderStubFactory.assertNoMoreCalls();
307 
308     // From Jython
309     m_interpreter.exec("z()");
310     m_recorderStubFactory.assertSuccess("start");
311     m_recorderStubFactory.assertSuccess("end", true);
312     m_recorderStubFactory.assertNoMoreCalls();
313 
314     // Other instances are not instrumented.
315     m_interpreter.exec("a=MyClass(1, 2, 3)");
316     m_interpreter.exec("a.getA()");
317     m_recorderStubFactory.assertNoMoreCalls();
318   }
319 
320   /**
321    * See bug 2992248.
322    */
323   @Test public void testJavaStaticMethod() throws Exception {
324     m_interpreter.exec("from grinder.test import MyClass");
325 
326     m_interpreter.exec(
327       "y=MyClass.staticSix\nz=MyClass.staticSix");
328     final PyObject pyJavaMethod = m_interpreter.get("y");
329     final PyObject pyJavaMethod2 = m_interpreter.get("z");
330     createInstrumentedProxy(m_test, m_recorder, pyJavaMethod);
331 
332     final PyObject result = pyJavaMethod.__call__();
333     assertEquals(m_six, result);
334     m_recorderStubFactory.assertSuccess("start");
335     m_recorderStubFactory.assertSuccess("end", true);
336     m_recorderStubFactory.assertNoMoreCalls();
337 
338     final Integer[] version = getJythonVersion();
339 
340     if (version[0] < 2 || version[1] < 5 || version[2] < 2) {
341       // Up to Jython 2.5.1, static bindings are resolved once...
342       assertSame(pyJavaMethod, pyJavaMethod2);
343     }
344     else {
345       assertNotSame(pyJavaMethod, pyJavaMethod2);
346     }
347 
348     // ... either way, z() is instrumented.
349 
350     final PyObject result2 = pyJavaMethod2.__call__();
351     assertEquals(m_six, result2);
352     m_recorderStubFactory.assertSuccess("start");
353     m_recorderStubFactory.assertSuccess("end", true);
354     m_recorderStubFactory.assertNoMoreCalls();
355   }
356 
357   @Test public void testJavaUnboundMethod() throws Exception {
358     m_interpreter.exec("from grinder.test import MyClass\n" +
359                        "x=MyClass(1, 2, 3)\n" +
360                        "y=MyClass(3, 2, 1)\n" +
361                        "m=MyClass.getA");
362 
363     final PyObject instance = m_interpreter.get("x");
364     final PyObject unboundMethod = m_interpreter.get("m");
365     createInstrumentedProxy(m_test, m_recorder, unboundMethod);
366 
367     final PyObject result = unboundMethod.__call__(instance);
368     assertEquals(m_one, result);
369     m_recorderStubFactory.assertSuccess("start");
370     m_recorderStubFactory.assertSuccess("end", true);
371     m_recorderStubFactory.assertNoMoreCalls();
372 
373     // Test instrumentation works through a separate binding.
374     final PyObject instance2 = m_interpreter.get("y");
375     final PyObject result2 = unboundMethod.__call__(instance2);
376     assertEquals(m_three, result2);
377     m_recorderStubFactory.assertSuccess("start");
378     m_recorderStubFactory.assertSuccess("end", true);
379     m_recorderStubFactory.assertNoMoreCalls();
380   }
381 
382   @Test public void testJavaBoundMethodSuperClassImplementation()
383     throws Exception {
384 
385     m_interpreter.exec("from grinder.test import MyExtendedClass\nx=MyExtendedClass()");
386 
387     m_interpreter.exec("y=x.getA");
388     final PyObject pyJavaMethod = m_interpreter.get("y");
389     createInstrumentedProxy(m_test, m_recorder, pyJavaMethod);
390 
391     final PyObject result = pyJavaMethod.__call__();
392     assertEquals(m_zero, result);
393     m_recorderStubFactory.assertSuccess("start");
394     m_recorderStubFactory.assertSuccess("end", true);
395     m_recorderStubFactory.assertNoMoreCalls();
396 
397     // Test instrumentation works through separate references.
398     m_interpreter.exec("z=x.getA");
399     final PyObject pyJavaMethod2 = m_interpreter.get("z");
400 
401     final PyObject result2 = pyJavaMethod2.__call__();
402     assertNotSame(result, result2);
403 
404     assertEquals(m_zero, result);
405     m_recorderStubFactory.assertSuccess("start");
406     m_recorderStubFactory.assertSuccess("end", true);
407     m_recorderStubFactory.assertNoMoreCalls();
408 
409     // From Jython
410     m_interpreter.exec("z()");
411     m_recorderStubFactory.assertSuccess("start");
412     m_recorderStubFactory.assertSuccess("end", true);
413     m_recorderStubFactory.assertNoMoreCalls();
414 
415     // Other instances are not instrumented.
416     m_interpreter.exec("a=MyExtendedClass()");
417     m_interpreter.exec("a.getA()");
418     m_recorderStubFactory.assertNoMoreCalls();
419   }
420 
421   @Test public void testJavaBoundMethodThroughInterface() throws Exception {
422     m_interpreter.exec("from grinder.test import MyExtendedClass\n"+
423                        "x=MyExtendedClass.create()");
424 
425     m_interpreter.exec("y=x.addOne");
426     final PyObject pyJavaMethod = m_interpreter.get("y");
427     createInstrumentedProxy(m_test, m_recorder, pyJavaMethod);
428 
429     final PyObject result = pyJavaMethod.__call__(m_one);
430     assertEquals(m_three, result);
431     m_recorderStubFactory.assertSuccess("start");
432     m_recorderStubFactory.assertSuccess("end", true);
433     m_recorderStubFactory.assertNoMoreCalls();
434 
435     // From Jython
436     m_interpreter.exec("x.addOne(123)");
437     m_recorderStubFactory.assertSuccess("start");
438     m_recorderStubFactory.assertSuccess("end", true);
439     m_recorderStubFactory.assertNoMoreCalls();
440 
441     // Other instances are not instrumented.
442     m_interpreter.exec("a=MyExtendedClass.create()");
443     m_interpreter.exec("a.addOne(1)");
444     m_recorderStubFactory.assertNoMoreCalls();
445   }
446 }