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.java;
23  import static java.util.Collections.singleton;
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertNull;
26  import static org.junit.Assert.assertSame;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  import static org.mockito.Matchers.any;
30  import static org.mockito.Mockito.doThrow;
31  import static org.mockito.Mockito.mock;
32  import static org.mockito.Mockito.times;
33  import static org.mockito.Mockito.verify;
34  import static org.mockito.Mockito.verifyNoMoreInteractions;
35  import static org.mockito.Mockito.when;
36  
37  import grinder.test.MyClass;
38  import grinder.test.MyExtendedClass;
39  
40  import java.lang.instrument.Instrumentation;
41  import java.lang.instrument.UnmodifiableClassException;
42  import java.lang.reflect.Method;
43  import java.net.URL;
44  import java.net.URLClassLoader;
45  import java.util.Collections;
46  
47  import net.grinder.engine.process.dcr.AnotherClass;
48  import net.grinder.engine.process.dcr.DCRContextImplementation;
49  import net.grinder.engine.process.dcr.RecorderLocatorAccess;
50  import net.grinder.script.NotWrappableTypeException;
51  import net.grinder.script.Test.InstrumentationFilter;
52  import net.grinder.scriptengine.Recorder;
53  import net.grinder.util.BlockingClassLoader;
54  import net.grinder.util.weave.agent.ExposeInstrumentation;
55  
56  import org.junit.After;
57  import org.junit.Before;
58  import org.junit.Test;
59  import org.mockito.Mock;
60  import org.mockito.MockitoAnnotations;
61  import org.slf4j.Logger;
62  
63  
64  
65  /**
66   * Unit tests for {@link JavaDCRInstrumenter}.
67   *
68   * @author Philip Aston
69   */
70  public class TestJavaDCRInstrumenter {
71  
72    @Mock private Logger m_logger;
73    @Mock private Recorder m_recorder;
74  
75    private JavaDCRInstrumenter m_instrumenter;
76    private Instrumentation m_originalInstrumentation;
77  
78    @Before public void setUp() throws Exception {
79      MockitoAnnotations.initMocks(this);
80  
81      m_instrumenter =
82        new JavaDCRInstrumenter(DCRContextImplementation.create(m_logger));
83      m_originalInstrumentation = ExposeInstrumentation.getInstrumentation();
84    }
85  
86    @After public void tearDown() throws Exception {
87      ExposeInstrumentation.premain("", m_originalInstrumentation);
88      RecorderLocatorAccess.clearRecorders();
89    }
90  
91    private void assertNotWrappable(Object o) throws Exception {
92      try {
93        m_instrumenter.createInstrumentedProxy(null, null, o);
94        fail("Expected NotWrappableTypeException");
95      }
96      catch (NotWrappableTypeException e) {
97      }
98    }
99  
100   @Test public void testGetDescription() throws Exception {
101     assertTrue(m_instrumenter.getDescription().length() > 0);
102   }
103 
104   @Test public void testCreateProxyWithNonWrappableParameters() throws Exception {
105 
106     assertNotWrappable(Object.class);
107     assertNotWrappable(new Object());
108     assertNotWrappable(new String());
109     assertNotWrappable(java.util.Random.class);
110 
111     // Can't wrap classes in net.grinder.engine.process.*
112     assertNotWrappable(new AnotherClass());
113   }
114 
115   @Test public void testInstrumentClass() throws Exception {
116 
117     assertEquals(6, MyClass.staticSix());
118 
119     final MyClass c1 = new MyClass();
120 
121     assertEquals(0, c1.getA());
122 
123     final Object result =
124       m_instrumenter.createInstrumentedProxy(null, m_recorder, MyClass.class);
125     assertSame(MyClass.class, result);
126 
127     MyClass.staticSix();
128 
129     verify(m_recorder).start();
130     verify(m_recorder).end(true);
131 
132     assertEquals(0, c1.getA());
133 
134     final MyClass c2 = new MyClass();
135     verify(m_recorder, times(3)).start();
136     verify(m_recorder, times(3)).end(true);
137     assertEquals(0, c1.getA());
138     assertEquals(0, c2.getA());
139 
140     m_instrumenter.createInstrumentedProxy(null,
141                                            m_recorder,
142                                            MyExtendedClass.class);
143 
144     MyClass.staticSix();
145 
146     // Single call - instrumenting extension shouldn't instrument superclass
147     // statics.
148     verify(m_recorder, times(4)).start();
149     verify(m_recorder, times(4)).end(true);
150     verifyNoMoreInteractions(m_recorder);
151   }
152 
153   @Test public void testInstrumentUnmodifiableCLass() throws Exception {
154 
155     final Instrumentation instrumentation = mock(Instrumentation.class);
156     when(instrumentation.isRetransformClassesSupported()).thenReturn(true);
157 
158     ExposeInstrumentation.premain("", instrumentation);
159 
160     doThrow(new UnmodifiableClassException())
161       .when(instrumentation).retransformClasses((Class<?>[]) any());
162 
163     instrumentation.retransformClasses(new Class[0]);
164 
165     // Create a new weaver to force the weaving.
166     final JavaDCRInstrumenter instrumenter =
167       new JavaDCRInstrumenter(DCRContextImplementation.create(m_logger));
168 
169     try {
170       instrumenter.createInstrumentedProxy(null, m_recorder, MyClass.class);
171       fail("Expected NotWrappableTypeException");
172     }
173     catch (NotWrappableTypeException e) {
174     }
175   }
176 
177   @Test public void testInstrumentInstance() throws Exception {
178 
179     RecorderLocatorAccess.clearRecorders();
180 
181     final MyClass c1 = new MyExtendedClass();
182 
183     assertEquals(0, c1.getA());
184 
185     final Object result =
186       m_instrumenter.createInstrumentedProxy(null, m_recorder, c1);
187     assertSame(c1, result);
188 
189     MyClass.staticSix();
190 
191     assertEquals(0, c1.getA());
192     verify(m_recorder).start();
193     verify(m_recorder).end(true);
194 
195     final MyClass c2 = new MyClass();
196     assertEquals(0, c2.getA());
197 
198     verifyNoMoreInteractions(m_recorder);
199   }
200 
201   @Test public void testSelectivelyInstrumentInstance() throws Exception {
202 
203     RecorderLocatorAccess.clearRecorders();
204 
205     final MyClass c1 = new MyExtendedClass();
206 
207     assertEquals(0, c1.getA());
208 
209     final InstrumentationFilter filter =
210       new InstrumentationFilter() {
211         public boolean matches(Object item) {
212           return ((Method)item).getName().equals("getA");
213         }
214     };
215 
216     m_instrumenter.instrument(null, m_recorder, c1, filter);
217 
218     MyClass.staticSix();
219 
220     assertEquals(0, c1.getA());
221     assertEquals(0, c1.getB());
222 
223     verify(m_recorder).start();
224     verify(m_recorder).end(true);
225 
226     final MyClass c2 = new MyClass();
227     assertEquals(0, c2.getA());
228 
229     verifyNoMoreInteractions(m_recorder);
230   }
231 
232 
233   @Test public void testWithNull() throws Exception {
234     assertNull(m_instrumenter.createInstrumentedProxy(null, null, null));
235   }
236 
237   @Test public void testArrays() throws Exception {
238 
239     RecorderLocatorAccess.clearRecorders();
240 
241     try {
242       m_instrumenter.createInstrumentedProxy(null, m_recorder, new MyClass[0]);
243       fail("Expected NotWrappableTypeException");
244     }
245     catch (NotWrappableTypeException e) {
246     }
247 
248     try {
249       m_instrumenter.createInstrumentedProxy(null, m_recorder, MyClass[].class);
250       fail("Expected NotWrappableTypeException");
251     }
252     catch (NotWrappableTypeException e) {
253     }
254 
255     verifyNoMoreInteractions(m_recorder);
256   }
257 
258   @Test public void testWithNoPackage() throws Exception {
259 
260     RecorderLocatorAccess.clearRecorders();
261 
262     final BlockingClassLoader blockingClassLoader =
263       new BlockingClassLoader(singleton(AnotherClass.class.getName()),
264                               Collections.<String>emptySet(),
265                               Collections.<String>emptySet(),
266                               true);
267 
268     final NoPackageURLClassLoader cl =
269       new NoPackageURLClassLoader(
270         ((URLClassLoader)getClass().getClassLoader()).getURLs(),
271         blockingClassLoader);
272 
273     final Class<?> noPackageClass = cl.loadClass(AnotherClass.class.getName());
274     final Method noPackageMethod = noPackageClass.getMethod("getOne");
275 
276     assertEquals(1, noPackageMethod.invoke(null));
277 
278     final Object result =
279       m_instrumenter.createInstrumentedProxy(null, m_recorder, noPackageClass);
280     assertSame(noPackageClass, result);
281 
282     assertEquals(1, noPackageMethod.invoke(null));
283     verify(m_recorder).start();
284     verify(m_recorder).end(true);
285     verifyNoMoreInteractions(m_recorder);
286   }
287 
288   private static class NoPackageURLClassLoader extends URLClassLoader {
289 
290     public NoPackageURLClassLoader(URL[] urls, ClassLoader parent) {
291       super(urls, parent);
292     }
293 
294     @Override
295     protected Package getPackage(String name) {
296       return null;
297     }
298   }
299 }
300