View Javadoc

1   // Copyright (C) 2005 - 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.engine.agent;
23  
24  import static net.grinder.testutility.AssertUtilities.assertContains;
25  import static org.junit.Assert.assertEquals;
26  import static org.junit.Assert.fail;
27  import static org.mockito.Mockito.mock;
28  
29  import java.io.File;
30  import java.io.InputStream;
31  import java.lang.instrument.Instrumentation;
32  
33  import net.grinder.common.GrinderProperties;
34  import net.grinder.communication.CommunicationException;
35  import net.grinder.communication.FanOutStreamSender;
36  import net.grinder.communication.StreamReceiver;
37  import net.grinder.engine.agent.DebugThreadWorker.IsolateGrinderProcessRunner;
38  import net.grinder.engine.common.EngineException;
39  import net.grinder.engine.common.ScriptLocation;
40  import net.grinder.engine.messages.InitialiseGrinderMessage;
41  import net.grinder.testutility.AbstractJUnit4FileTestCase;
42  import net.grinder.testutility.AssertUtilities;
43  import net.grinder.testutility.RedirectStandardStreams;
44  import net.grinder.util.weave.agent.ExposeInstrumentation;
45  
46  import org.junit.After;
47  import org.junit.Test;
48  
49  
50  /**
51   * Unit tests for {@link DebugThreadWorkerFactory}/
52   *
53   * @author Philip Aston
54   */
55  public class TestDebugThreadWorkerFactory extends AbstractJUnit4FileTestCase {
56  
57    private AgentIdentityImplementation m_agentIdentity =
58      new AgentIdentityImplementation(getClass().getName());
59  
60    private FanOutStreamSender m_fanOutStreamSender = new FanOutStreamSender(1);
61    private GrinderProperties m_properties = new GrinderProperties();
62  
63    @After public void shutdownStreamSender() throws Exception {
64      m_fanOutStreamSender.shutdown();
65    }
66  
67    @Test public void testFactory() throws Exception {
68      m_properties.setProperty("grinder.logDirectory",
69                             getDirectory().getAbsolutePath());
70  
71      final DebugThreadWorkerFactory factory =
72        new DebugThreadWorkerFactory(m_agentIdentity,
73                                     m_fanOutStreamSender,
74                                     false,
75                                     new ScriptLocation(new File("missing.py")),
76                                     m_properties);
77  
78      final RedirectStandardStreams streams = new RedirectStandardStreams() {
79        protected void runWithRedirectedStreams() throws Exception {
80          final Worker worker = factory.create(null, null);
81          worker.waitFor();
82        }
83      }.run();
84  
85      streams.assertNoStdout();
86  
87      assertContains(new String(streams.getStderrBytes()), "File not found");
88  
89      // Should have and output and a data file.
90      final File[] files = getDirectory().listFiles();
91  
92      assertEquals(2, files.length);
93    }
94  
95    @Test public void testWithBadIsolatedRunner() throws Exception {
96      try {
97        DebugThreadWorkerFactory.setIsolatedRunnerClass(
98          BadClassInaccesible.class.getName());
99  
100       final DebugThreadWorkerFactory factory =
101         new DebugThreadWorkerFactory(m_agentIdentity,
102                                      m_fanOutStreamSender,
103                                      false,
104                                      new ScriptLocation(new File(".")),
105                                      m_properties);
106 
107       try {
108         factory.create(null, null);
109         fail("Expected EngineException");
110       }
111       catch (EngineException e) {
112       }
113     }
114     finally {
115       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
116     }
117   }
118 
119   @Test public void testWithBadIsolatedRunner2() throws Exception {
120     try {
121       DebugThreadWorkerFactory.setIsolatedRunnerClass(
122         BadClassCantInstantiate.class.getName());
123 
124       final DebugThreadWorkerFactory factory2 =
125         new DebugThreadWorkerFactory(m_agentIdentity,
126                                      m_fanOutStreamSender,
127                                      false,
128                                      new ScriptLocation(new File(".")),
129                                      m_properties);
130 
131       try {
132         factory2.create(null, null);
133         fail("Expected EngineException");
134       }
135       catch (EngineException e) {
136       }
137     }
138     finally {
139       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
140     }
141   }
142 
143   @Test public void testWithBadIsolatedRunner3() throws Exception {
144     try {
145       DebugThreadWorkerFactory.setIsolatedRunnerClass(
146         BadClassNotAnIsolateGrinderProcessRunner.class.getName());
147 
148       final DebugThreadWorkerFactory factory3 =
149         new DebugThreadWorkerFactory(m_agentIdentity,
150                                      m_fanOutStreamSender,
151                                      false,
152                                      new ScriptLocation(new File(".")),
153                                      m_properties);
154 
155       try {
156         factory3.create(null, null);
157         fail("Expected ClassCastException");
158       }
159       catch (ClassCastException e) {
160       }
161     }
162     finally {
163       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
164     }
165   }
166 
167   @Test public void testWithBadIsolatedRunner4() throws Exception {
168     try {
169       DebugThreadWorkerFactory.setIsolatedRunnerClass("Not a class");
170 
171       final DebugThreadWorkerFactory factory3 =
172         new DebugThreadWorkerFactory(m_agentIdentity,
173                                      m_fanOutStreamSender,
174                                      false,
175                                      new ScriptLocation(new File(".")),
176                                      m_properties);
177 
178       try {
179         factory3.create(null, null);
180         fail("Expected AssertionError");
181       }
182       catch (AssertionError e) {
183       }
184     }
185     finally {
186       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
187     }
188   }
189 
190   @Test public void testIsolation() throws Exception {
191     try {
192       DebugThreadWorkerFactory
193         .setIsolatedRunnerClass(GoodRunner.class.getName());
194 
195       final DebugThreadWorkerFactory factory =
196         new DebugThreadWorkerFactory(m_agentIdentity,
197                                      m_fanOutStreamSender,
198                                      false,
199                                      new ScriptLocation(new File(".")),
200                                      m_properties);
201 
202       final RedirectStandardStreams rss0 = new RedirectStandardStreams() {
203         protected void runWithRedirectedStreams() throws Exception {
204           final Worker worker = factory.create(null, null);
205           worker.waitFor();
206         }
207       };
208 
209       rss0.run();
210 
211       final RedirectStandardStreams rss1 = new RedirectStandardStreams() {
212         protected void runWithRedirectedStreams() throws Exception {
213           final Worker worker = factory.create(null, null);
214           worker.waitFor();
215         }
216       };
217 
218       rss1.run();
219 
220       final String worker0Result = new String(rss0.getStdoutBytes());
221       final String worker1Result = new String(rss1.getStdoutBytes());
222 
223       AssertUtilities.assertContains(worker0Result, "Hello from 0 count is 1");
224       AssertUtilities.assertContains(worker1Result, "Hello from 1 count is 1");
225     }
226     finally {
227       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
228     }
229   }
230 
231   @Test public void testIsolationWithSharedClasses() throws Exception {
232     try {
233       m_properties.setProperty("grinder.debug.singleprocess.sharedclasses",
234                                MyStaticHolder.class.getName());
235 
236       DebugThreadWorkerFactory
237         .setIsolatedRunnerClass(GoodRunner.class.getName());
238 
239       final DebugThreadWorkerFactory factory =
240         new DebugThreadWorkerFactory(m_agentIdentity,
241                                      m_fanOutStreamSender,
242                                      false,
243                                      new ScriptLocation(new File(".")),
244                                      m_properties);
245 
246       final RedirectStandardStreams rss0 = new RedirectStandardStreams() {
247         protected void runWithRedirectedStreams() throws Exception {
248           final Worker worker = factory.create(null, null);
249           worker.waitFor();
250         }
251       };
252 
253       rss0.run();
254 
255       final RedirectStandardStreams rss1 = new RedirectStandardStreams() {
256         protected void runWithRedirectedStreams() throws Exception {
257           final Worker worker = factory.create(null, null);
258           worker.waitFor();
259         }
260       };
261 
262       rss1.run();
263 
264       final String worker0Result = new String(rss0.getStdoutBytes());
265       final String worker1Result = new String(rss1.getStdoutBytes());
266 
267       AssertUtilities.assertContains(worker0Result, "Hello from 0 count is 1");
268       AssertUtilities.assertContains(worker1Result, "Hello from 1 count is 2");
269     }
270     finally {
271       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
272     }
273   }
274 
275   // Bug 2958145.
276   @Test public void testExposeInstrumentationNotIsolated() throws Exception {
277 
278     final Instrumentation originalInstrumentation =
279       ExposeInstrumentation.getInstrumentation();
280 
281     final Instrumentation instrumentation = mock(Instrumentation.class);
282 
283     DebugThreadWorkerFactory.setIsolatedRunnerClass(
284       AccessInstrumentationRunner.class.getName());
285 
286     try {
287       ExposeInstrumentation.premain("", instrumentation);
288 
289       final DebugThreadWorkerFactory factory =
290         new DebugThreadWorkerFactory(m_agentIdentity,
291                                      m_fanOutStreamSender,
292                                      false,
293                                      new ScriptLocation(new File(".")),
294                                      m_properties);
295 
296       final RedirectStandardStreams rss0 = new RedirectStandardStreams() {
297         protected void runWithRedirectedStreams() throws Exception {
298           final Worker worker = factory.create(null, null);
299           worker.waitFor();
300         }
301       };
302 
303       rss0.run();
304 
305       final String worker0Result = new String(rss0.getStdoutBytes());
306 
307       AssertUtilities.assertContains(
308         worker0Result,
309         Integer.toString(instrumentation.hashCode()));
310     }
311     finally {
312       DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
313       ExposeInstrumentation.premain("", originalInstrumentation);
314     }
315   }
316 
317 
318   public static class BadClassInaccesible {
319     BadClassInaccesible() { }
320   }
321 
322   public static abstract class BadClassCantInstantiate {
323     public BadClassCantInstantiate() { }
324   }
325 
326   public static class BadClassNotAnIsolateGrinderProcessRunner { }
327 
328   public static class MyStaticHolder {
329     private static int s_number = 0;
330 
331     public static void incrementNumber() {
332       ++s_number;
333     }
334 
335     public static int getNumber() {
336       return s_number;
337     }
338   }
339 
340   public abstract static class AbstractGoodRunner
341     implements IsolateGrinderProcessRunner {
342     protected InitialiseGrinderMessage initialise(InputStream inputStream) {
343 
344       final StreamReceiver streamReceiver = new StreamReceiver(inputStream);
345 
346       try {
347         return (InitialiseGrinderMessage)streamReceiver.waitForMessage();
348       }
349       catch (CommunicationException e) {
350         throw new AssertionError(e);
351       }
352     }
353   }
354 
355   public static class GoodRunner extends AbstractGoodRunner{
356     public int run(InputStream agentInputStream) {
357       final InitialiseGrinderMessage message = initialise(agentInputStream);
358 
359       MyStaticHolder.incrementNumber();
360 
361       System.out.println(
362         "Hello from " + message.getWorkerIdentity().getNumber() +
363         " count is " + MyStaticHolder.getNumber());
364 
365       return 0;
366     }
367   }
368 
369   public static class AccessInstrumentationRunner
370     extends AbstractGoodRunner {
371 
372     public int run(InputStream agentInputStream) {
373       initialise(agentInputStream);
374 
375       System.out.println(ExposeInstrumentation.getInstrumentation().hashCode());
376 
377       return 0;
378     }
379   }
380 }