View Javadoc

1   // Copyright (C) 2005 - 2012 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;
23  
24  import static net.grinder.testutility.AssertUtilities.assertContains;
25  import static net.grinder.testutility.FileUtilities.createFile;
26  import static org.junit.Assert.assertEquals;
27  import static org.junit.Assert.assertNotSame;
28  import static org.junit.Assert.assertSame;
29  import static org.junit.Assert.assertTrue;
30  import static org.junit.Assert.fail;
31  
32  import java.io.File;
33  
34  import net.grinder.engine.common.EngineException;
35  import net.grinder.engine.common.ScriptLocation;
36  import net.grinder.scriptengine.ScriptEngineService.ScriptEngine;
37  import net.grinder.scriptengine.ScriptEngineService.WorkerRunnable;
38  import net.grinder.testutility.AbstractJUnit4FileTestCase;
39  import net.grinder.util.Directory;
40  
41  import org.junit.Before;
42  import org.junit.Test;
43  import org.python.core.PyObject;
44  import org.python.core.PySystemState;
45  import org.python.util.PythonInterpreter;
46  
47  
48  /**
49   * Unit tests for {@link JythonScriptEngine}.
50   *
51   * @author Philip Aston
52   */
53  public class TestJythonScriptEngine extends AbstractJUnit4FileTestCase {
54  
55    private static Object s_lastCallbackObject;
56  
57    {
58      PySystemState.initialize();
59    }
60  
61    private final PythonInterpreter m_interpreter =
62      new PythonInterpreter(null, new PySystemState());
63  
64    private ScriptLocation m_script;
65  
66    @Before public void initialise() throws Exception {
67      final File scriptFile = new File(getDirectory(), "script");
68  
69      m_script = new ScriptLocation(new Directory(getDirectory()), scriptFile);
70    }
71  
72    @Test public void testInitialiseNoFile() throws Exception {
73      try {
74        new JythonScriptEngine(m_script);
75        fail("Expected JythonScriptExecutionException");
76      }
77      catch (final JythonScriptExecutionException e) {
78        assertContains(e.getShortMessage(), "IOError");
79      }
80    }
81  
82    @Test public void testInitialiseNoCallable() throws Exception {
83      createFile(m_script.getFile());
84  
85      try {
86        new JythonScriptEngine(m_script);
87        fail("Expected EngineException");
88      }
89      catch (final EngineException e) {
90        assertContains(e.getMessage(), "no callable");
91      }
92    }
93  
94    @Test public void testInitialiseNoCallable2() throws Exception {
95      createFile(m_script.getFile(),
96                 "TestRunner = 1");
97  
98      try {
99        new JythonScriptEngine(m_script);
100       fail("Expected EngineException");
101     }
102     catch (final EngineException e) {
103       assertContains(e.getMessage(), "no callable");
104     }
105   }
106 
107   @Test public void testInitialise() throws Exception {
108     createFile(m_script.getFile(),
109                "class TestRunner:pass");
110 
111     final ScriptEngine scriptEngine = new JythonScriptEngine(m_script);
112     assertContains(scriptEngine.getDescription(), "Jython");
113     scriptEngine.shutdown();
114   }
115 
116   @Test public void testInitialiseJythonException() throws Exception {
117     final File directory = new File(getDirectory(), "bah/foo");
118     assertTrue(directory.mkdirs());
119 
120     // Bad root directory, causes import to fail.
121     final ScriptLocation script =
122       new ScriptLocation(new Directory(new File("bah")),
123                          new File(getDirectory(), "script"));
124 
125     createFile(script.getFile(),
126                "import foo",
127                "class TestRunner:pass");
128 
129     try {
130       new JythonScriptEngine(script);
131       fail("Expected JythonScriptExecutionException");
132     }
133     catch (final JythonScriptExecutionException e) {
134       assertContains(e.getShortMessage(), "ImportError");
135     }
136   }
137 
138   @Test public void testInitialisePathScriptWorkingDirectory()
139       throws Exception {
140     final File directory = new File(getDirectory(), "bah/foo");
141     assertTrue(directory.mkdirs());
142 
143     // Import works from ScriptLocation directory.
144     final ScriptLocation script =
145         new ScriptLocation(new Directory(new File(getDirectory(), "bah")),
146                            new File(getDirectory(), "script"));
147 
148     createFile(script.getFile(),
149                "import foo",
150                "class TestRunner:pass");
151 
152     final ScriptEngine scriptEngine = new JythonScriptEngine(script);
153     scriptEngine.shutdown();
154   }
155 
156   @Test public void testInitialisePathScriptDirectory() throws Exception {
157     final File directory = new File(getDirectory(), "bah/foo");
158     assertTrue(directory.mkdirs());
159 
160     // Import works from script directory.
161     final ScriptLocation script =
162         new ScriptLocation(new Directory(new File("bah")),
163                            new File(getDirectory(), "script"));
164 
165     createFile(script.getFile(),
166                "import bah.foo",
167                "class TestRunner:pass");
168 
169     final ScriptEngine scriptEngine = new JythonScriptEngine(script);
170     scriptEngine.shutdown();
171   }
172 
173 
174   @Test public void testShutdownExitHook() throws Exception {
175 
176     callback(null);
177 
178     createFile(
179       m_script.getFile(),
180       "from net.grinder.scriptengine.jython import TestJythonScriptEngine",
181       "import sys",
182       "def f():",
183       " TestJythonScriptEngine.callback(TestJythonScriptEngine)",
184       "sys.exitfunc = f",
185       "class TestRunner:pass");
186 
187     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
188     scriptEngine.shutdown();
189 
190     assertSame(TestJythonScriptEngine.class, s_lastCallbackObject);
191   }
192 
193   @Test public void testShutdownBadHook() throws Exception {
194 
195     createFile(
196       m_script.getFile(),
197       "import sys",
198       "def f():",
199       " raise Exception('a problem')",
200       "sys.exitfunc = f",
201       "class TestRunner:pass");
202 
203     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
204 
205     try {
206       scriptEngine.shutdown();
207       fail("Expected JythonScriptExecutionException");
208     }
209     catch (final JythonScriptExecutionException e) {
210       assertContains(e.getShortMessage(), "a problem");
211     }
212   }
213 
214   @Test public void testWorkerRunnableNoCallable() throws Exception {
215 
216     createFile(
217       m_script.getFile(),
218       "class TestRunner:pass");
219 
220     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
221 
222     try {
223       scriptEngine.createWorkerRunnable();
224       fail("Expected EngineException");
225     }
226     catch (final EngineException e) {
227       assertContains(e.getMessage(), "is not callable");
228     }
229   }
230 
231   @Test public void testWorkerRunnableBadRunner() throws Exception {
232 
233     createFile(
234       m_script.getFile(),
235       "class TestRunner:",
236       " def __init__(self): raise Exception('a problem')"
237       );
238 
239     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
240 
241     try {
242       scriptEngine.createWorkerRunnable();
243       fail("Expected JythonScriptExecutionException");
244     }
245     catch (final JythonScriptExecutionException e) {
246       assertContains(e.getShortMessage(), "a problem");
247     }
248   }
249 
250   @Test public void testWorkerRunnable() throws Exception {
251 
252     createFile(
253       m_script.getFile(),
254       "class TestRunner:",
255       " def __call__(self): pass"
256       );
257 
258     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
259     final WorkerRunnable runnable1 = scriptEngine.createWorkerRunnable();
260     final WorkerRunnable runnable2 = scriptEngine.createWorkerRunnable();
261     assertNotSame(runnable1, runnable2);
262     runnable1.run();
263     runnable2.run();
264 
265     runnable1.shutdown();
266     runnable2.shutdown();
267 
268     scriptEngine.shutdown();
269   }
270 
271   @Test public void testWorkerRunnableBadRunner2() throws Exception {
272 
273     createFile(
274       m_script.getFile(),
275       "class TestRunner:",
276       " def __call__(self): raise Exception('a problem')");
277 
278     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
279     final WorkerRunnable runnable = scriptEngine.createWorkerRunnable();
280 
281     try {
282       runnable.run();
283       fail("Expected JythonScriptExecutionException");
284     }
285     catch (final JythonScriptExecutionException e) {
286       assertContains(e.getShortMessage(), "a problem");
287     }
288   }
289 
290   @Test public void testWorkerRunnableBadRunner3() throws Exception {
291 
292     createFile(
293       m_script.getFile(),
294       "class TestRunner:",
295       " def __call__(self): pass",
296       " def __del__(self): raise Exception('a problem')");
297 
298     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
299     final WorkerRunnable runnable = scriptEngine.createWorkerRunnable();
300 
301     try {
302       runnable.shutdown();
303       fail("Expected JythonScriptExecutionException");
304     }
305     catch (final JythonScriptExecutionException e) {
306       assertContains(e.getShortMessage(), "a problem");
307     }
308 
309     // Try it again, __del__ should now be disabled.
310     runnable.shutdown();
311   }
312 
313   @Test public void testNewWorkerRunnableWithTestRunner() throws Exception {
314     createFile(
315       m_script.getFile(),
316       "class TestRunner: pass");
317 
318     final JythonScriptEngine scriptEngine = new JythonScriptEngine(m_script);
319 
320     try {
321       scriptEngine.createWorkerRunnable(null);
322       fail("Expected JythonScriptExecutionException");
323     }
324     catch (final JythonScriptExecutionException e) {
325       assertContains(e.getMessage(), "is not callable");
326     }
327 
328     final Object badRunner = new Object();
329 
330     try {
331       scriptEngine.createWorkerRunnable(badRunner);
332       fail("Expected JythonScriptExecutionException");
333     }
334     catch (final JythonScriptExecutionException e) {
335       assertContains(e.getMessage(), "is not callable");
336     }
337 
338     m_interpreter.exec("result=1");
339     m_interpreter.exec("def myRunner():\n global result\n result=99");
340     final PyObject goodRunner = m_interpreter.get("myRunner");
341 
342     final WorkerRunnable workerRunnable =
343       scriptEngine.createWorkerRunnable(goodRunner);
344 
345     assertEquals("1", m_interpreter.get("result").toString());
346 
347     workerRunnable.run();
348     assertEquals("99", m_interpreter.get("result").toString());
349 
350     final PyObject badRunner2 =  m_interpreter.get("result");
351 
352     try {
353       scriptEngine.createWorkerRunnable(badRunner2);
354       fail("Expected JythonScriptExecutionException");
355     }
356     catch (final JythonScriptExecutionException e) {
357       assertContains(e.getMessage(), "is not callable");
358     }
359   }
360 
361   public static void callback(final Object o) {
362     s_lastCallbackObject = o;
363   }
364 }