comparison tests/test_pymonkey.py @ 117:ac8ca0ee7760

Moved all .cpp/.h files into 'src' dir and test suite into 'tests' dir.
author Atul Varma <varmaa@toolness.com>
date Mon, 17 Aug 2009 22:08:33 -0700
parents test_pymonkey.py@00c1351b3e82
children 856ca7a139e4
comparison
equal deleted inserted replaced
116:06269ca0b36c 117:ac8ca0ee7760
1 import sys
2 import unittest
3 import weakref
4 import time
5 import threading
6
7 import pymonkey
8
9 class PymonkeyTests(unittest.TestCase):
10 def _evaljs(self, code):
11 rt = pymonkey.Runtime()
12 cx = rt.new_context()
13 obj = cx.new_object()
14 cx.init_standard_classes(obj)
15 return cx.evaluate_script(obj, code, '<string>', 1)
16
17 def _evalJsWrappedPyFunc(self, func, code):
18 cx = pymonkey.Runtime().new_context()
19 obj = cx.new_object()
20 cx.init_standard_classes(obj)
21 jsfunc = cx.new_function(func, func.__name__)
22 cx.define_property(obj, func.__name__, jsfunc)
23 return cx.evaluate_script(obj, code, '<string>', 1)
24
25 def assertRaises(self, exctype, func, *args):
26 was_raised = False
27 try:
28 func(*args)
29 except exctype, e:
30 self.last_exception = e
31 was_raised = True
32 self.assertTrue(was_raised)
33
34 def testThreadSafetyExceptionIsRaised(self):
35 stuff = {}
36 def make_runtime():
37 stuff['rt'] = pymonkey.Runtime()
38 thread = threading.Thread(target = make_runtime)
39 thread.start()
40 thread.join()
41 self.assertRaises(pymonkey.error,
42 stuff['rt'].new_context)
43 self.assertEqual(self.last_exception.args[0],
44 'Function called from wrong thread')
45
46 def testClearObjectPrivateWorks(self):
47 class Foo(object):
48 pass
49 pyobj = Foo()
50 cx = pymonkey.Runtime().new_context()
51 obj = cx.new_object(pyobj)
52 pyobj = weakref.ref(pyobj)
53 self.assertEqual(pyobj(), cx.get_object_private(obj))
54 cx.clear_object_private(obj)
55 self.assertEqual(cx.get_object_private(obj), None)
56 self.assertEqual(pyobj(), None)
57
58 def testGetObjectPrivateWorks(self):
59 class Foo(object):
60 pass
61 pyobj = Foo()
62 cx = pymonkey.Runtime().new_context()
63 obj = cx.new_object(pyobj)
64 pyobj = weakref.ref(pyobj)
65 self.assertEqual(pyobj(), cx.get_object_private(obj))
66 del obj
67 del cx
68 self.assertEqual(pyobj(), None)
69
70 def testOperationCallbackIsCalled(self):
71 def opcb(cx):
72 raise Exception("stop eet!")
73
74 cx = pymonkey.Runtime().new_context()
75 cx.set_operation_callback(opcb)
76 obj = cx.new_object()
77 cx.init_standard_classes(obj)
78
79 def watchdog():
80 time.sleep(0.1)
81 cx.trigger_operation_callback()
82
83 thread = threading.Thread(target = watchdog)
84 thread.start()
85
86 self.assertRaises(
87 pymonkey.error,
88 cx.evaluate_script,
89 obj, 'while (1) {}', '<string>', 1
90 )
91
92 def testUndefinedStrIsUndefined(self):
93 self.assertEqual(str(pymonkey.undefined),
94 "pymonkey.undefined")
95
96 def testJsWrappedPythonFuncHasPrivate(self):
97 def foo(cx, this, args):
98 pass
99
100 cx = pymonkey.Runtime().new_context()
101 jsfunc = cx.new_function(foo, foo.__name__)
102 self.assertEqual(cx.get_object_private(jsfunc), foo)
103
104 def testJsWrappedPythonFuncIsNotGCd(self):
105 def define(cx, obj):
106 def func(cx, this, args):
107 return u'func was called'
108 jsfunc = cx.new_function(func, func.__name__)
109 cx.define_property(obj, func.__name__, jsfunc)
110 return weakref.ref(func)
111 rt = pymonkey.Runtime()
112 cx = rt.new_context()
113 obj = cx.new_object()
114 cx.init_standard_classes(obj)
115 ref = define(cx, obj)
116 cx.gc()
117 self.assertNotEqual(ref(), None)
118 result = cx.evaluate_script(obj, 'func()', '<string>', 1)
119 self.assertEqual(result, u'func was called')
120
121 # Now ensure that the wrapped function is GC'd when it's
122 # no longer reachable from JS space.
123 cx.define_property(obj, 'func', 0)
124 cx.gc()
125 self.assertEqual(ref(), None)
126
127 def testCircularJsWrappedPythonFuncIsGCdIfPrivateCleared(self):
128 def define(cx, obj):
129 rt = cx.get_runtime()
130 def func(cx, this, args):
131 # Oh noes, a circular reference is born!
132 rt
133 jsfunc = cx.new_function(func, func.__name__)
134 cx.define_property(obj, func.__name__, jsfunc)
135 return (jsfunc, weakref.ref(func))
136 rt = pymonkey.Runtime()
137 cx = rt.new_context()
138 obj = cx.new_object()
139 cx.init_standard_classes(obj)
140 jsfunc, ref = define(cx, obj)
141
142 # This will break the circular reference.
143 cx.clear_object_private(jsfunc)
144
145 del jsfunc
146 del rt
147 del cx
148 del obj
149 self.assertEqual(ref(), None)
150
151 def testJsWrappedPythonFuncIsGCdAtRuntimeDestruction(self):
152 def define(cx, obj):
153 def func(cx, this, args):
154 return u'func was called'
155 jsfunc = cx.new_function(func, func.__name__)
156 cx.define_property(obj, func.__name__, jsfunc)
157 return weakref.ref(func)
158 rt = pymonkey.Runtime()
159 cx = rt.new_context()
160 obj = cx.new_object()
161 cx.init_standard_classes(obj)
162 ref = define(cx, obj)
163 del rt
164 del cx
165 del obj
166 self.assertEqual(ref(), None)
167
168 def testJsWrappedPythonFuncThrowsExcIfPrivateCleared(self):
169 def func(cx, this, args):
170 return True
171
172 code = "func()"
173 cx = pymonkey.Runtime().new_context()
174 obj = cx.new_object()
175 cx.init_standard_classes(obj)
176 jsfunc = cx.new_function(func, func.__name__)
177 cx.define_property(obj, func.__name__, jsfunc)
178 cx.clear_object_private(jsfunc)
179 self.assertRaises(pymonkey.error,
180 cx.evaluate_script,
181 obj, code, '<string>', 1)
182 self.assertEqual(
183 self._tostring(cx, self.last_exception.args[0]),
184 "Error: Wrapped Python function no longer exists"
185 )
186
187 def testJsWrappedPythonFuncPassesContext(self):
188 contexts = []
189
190 def func(cx, this, args):
191 contexts.append(cx)
192 return True
193
194 code = "func()"
195 cx = pymonkey.Runtime().new_context()
196 obj = cx.new_object()
197 cx.init_standard_classes(obj)
198 jsfunc = cx.new_function(func, func.__name__)
199 cx.define_property(obj, func.__name__, jsfunc)
200 cx.evaluate_script(obj, code, '<string>', 1)
201 self.assertEqual(contexts[0], cx)
202
203 def testJsWrappedPythonFuncPassesThisArg(self):
204 thisObjs = []
205
206 def func(cx, this, args):
207 thisObjs.append(this)
208 return True
209
210 code = "func()"
211 cx = pymonkey.Runtime().new_context()
212 obj = cx.new_object()
213 cx.init_standard_classes(obj)
214 jsfunc = cx.new_function(func, func.__name__)
215 cx.define_property(obj, func.__name__, jsfunc)
216 cx.evaluate_script(obj, code, '<string>', 1)
217 self.assertEqual(thisObjs[0], obj)
218
219 def testJsWrappedPythonFuncPassesFuncArgs(self):
220 funcArgs = []
221
222 def func(cx, this, args):
223 funcArgs.append(args)
224 return True
225
226 cx = pymonkey.Runtime().new_context()
227 obj = cx.new_object()
228 cx.init_standard_classes(obj)
229 jsfunc = cx.new_function(func, func.__name__)
230 cx.define_property(obj, func.__name__, jsfunc)
231
232 cx.evaluate_script(obj, "func()", '<string>', 1)
233 self.assertEqual(len(funcArgs[0]), 0)
234 self.assertTrue(isinstance(funcArgs[0], tuple))
235
236 cx.evaluate_script(obj, "func(1, 'foo')", '<string>', 1)
237 self.assertEqual(len(funcArgs[1]), 2)
238 self.assertEqual(funcArgs[1][0], 1)
239 self.assertEqual(funcArgs[1][1], u'foo')
240
241 def testJsWrappedPythonFunctionReturnsUnicodeWithEmbeddedNULs(self):
242 def hai2u(cx, this, args):
243 return args[0] + u"o hai"
244 self.assertEqual(self._evalJsWrappedPyFunc(hai2u,
245 'hai2u("blah\x00 ")'),
246 u"blah\x00 o hai")
247
248 def testJsWrappedPythonFunctionReturnsString(self):
249 def hai2u(cx, this, args):
250 return "o hai"
251 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
252 "o hai")
253
254 def testJsWrappedPythonFunctionReturnsUnicode(self):
255 def hai2u(cx, this, args):
256 return u"o hai\u2026"
257 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
258 u"o hai\u2026")
259
260 def testJsWrappedPythonFunctionThrowsJsException(self):
261 def hai2u(cx, this, args):
262 raise pymonkey.error(u"blarg")
263 self.assertRaises(pymonkey.error,
264 self._evalJsWrappedPyFunc,
265 hai2u, 'hai2u()')
266 self.assertEqual(self.last_exception.args[0], u"blarg")
267
268 def testJsWrappedPythonFunctionThrowsPyException(self):
269 thecx = []
270 def hai2u(cx, this, args):
271 thecx.append(cx)
272 raise Exception("hello")
273 self.assertRaises(pymonkey.error,
274 self._evalJsWrappedPyFunc,
275 hai2u, 'hai2u()')
276 exc = thecx[0].get_object_private(self.last_exception.args[0])
277 self.assertEqual(exc.args[0], "hello")
278
279 def testJsWrappedPythonFunctionReturnsNone(self):
280 def hai2u(cx, this, args):
281 pass
282 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
283 None)
284
285 def testJsWrappedPythonFunctionReturnsTrue(self):
286 def hai2u(cx, this, args):
287 return True
288 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
289 True)
290
291 def testJsWrappedPythonFunctionReturnsFalse(self):
292 def hai2u(cx, this, args):
293 return False
294 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
295 False)
296
297 def testJsWrappedPythonFunctionReturnsSmallInt(self):
298 def hai2u(cx, this, args):
299 return 5
300 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
301 5)
302
303 def testJsWrappedPythonFunctionReturnsFloat(self):
304 def hai2u(cx, this, args):
305 return 5.1
306 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
307 5.1)
308
309 def testJsWrappedPythonFunctionReturnsNegativeInt(self):
310 def hai2u(cx, this, args):
311 return -5
312 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
313 -5)
314
315 def testJsWrappedPythonFunctionReturnsBigInt(self):
316 def hai2u(cx, this, args):
317 return 2147483647
318 self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'),
319 2147483647)
320
321 def testDefinePropertyWorksWithUnicodePropertyNames(self):
322 cx = pymonkey.Runtime().new_context()
323 obj = cx.new_object()
324 cx.init_standard_classes(obj)
325 foo = cx.new_object()
326 cx.define_property(obj, u"foo\u2026", foo)
327 self.assertEqual(
328 cx.get_property(obj, u"foo\u2026"),
329 foo
330 )
331
332 def testDefinePropertyWorksWithObject(self):
333 cx = pymonkey.Runtime().new_context()
334 obj = cx.new_object()
335 cx.init_standard_classes(obj)
336 foo = cx.new_object()
337 cx.define_property(obj, "foo", foo)
338 self.assertEqual(
339 cx.evaluate_script(obj, 'foo', '<string>', 1),
340 foo
341 )
342
343 def testDefinePropertyWorksWithString(self):
344 cx = pymonkey.Runtime().new_context()
345 obj = cx.new_object()
346 cx.init_standard_classes(obj)
347 foo = cx.new_object()
348 cx.define_property(obj, "foo", u"hello")
349 self.assertEqual(
350 cx.evaluate_script(obj, 'foo', '<string>', 1),
351 u"hello"
352 )
353
354 def testObjectIsIdentityPreserving(self):
355 cx = pymonkey.Runtime().new_context()
356 obj = cx.new_object()
357 cx.init_standard_classes(obj)
358 cx.evaluate_script(obj, 'var foo = {bar: 1}', '<string>', 1)
359 self.assertTrue(isinstance(cx.get_property(obj, u"foo"),
360 pymonkey.Object))
361 self.assertTrue(cx.get_property(obj, u"foo") is
362 cx.get_property(obj, "foo"))
363
364 def testObjectGetattrThrowsException(self):
365 cx = pymonkey.Runtime().new_context()
366 obj = cx.new_object()
367 cx.init_standard_classes(obj)
368 result = cx.evaluate_script(obj, '({get foo() { throw "blah"; }})',
369 '<string>', 1)
370 self.assertRaises(pymonkey.error,
371 cx.get_property,
372 result,
373 u"foo")
374 self.assertEqual(self.last_exception.args[0], u"blah")
375
376 def testInfiniteRecursionRaisesError(self):
377 cx = pymonkey.Runtime().new_context()
378 obj = cx.new_object()
379 cx.init_standard_classes(obj)
380 self.assertRaises(
381 pymonkey.error,
382 cx.evaluate_script,
383 obj, '(function foo() { foo(); })();', '<string>', 1
384 )
385 self.assertEqual(
386 self._tostring(cx, self.last_exception.args[0]),
387 "InternalError: too much recursion"
388 )
389
390 def testObjectGetattrWorks(self):
391 cx = pymonkey.Runtime().new_context()
392 obj = cx.new_object()
393 cx.init_standard_classes(obj)
394 cx.evaluate_script(obj, 'var boop = 5', '<string>', 1)
395 cx.evaluate_script(obj, 'this["blarg\u2026"] = 5', '<string>', 1)
396 self.assertEqual(cx.get_property(obj, u"beans"),
397 pymonkey.undefined)
398 self.assertEqual(cx.get_property(obj, u"blarg\u2026"), 5)
399 self.assertEqual(cx.get_property(obj, u"boop"), 5)
400
401 def testContextIsInstance(self):
402 cx = pymonkey.Runtime().new_context()
403 self.assertTrue(isinstance(cx, pymonkey.Context))
404
405 def testContextTypeCannotBeInstantiated(self):
406 self.assertRaises(TypeError, pymonkey.Context)
407
408 def testObjectIsInstance(self):
409 obj = pymonkey.Runtime().new_context().new_object()
410 self.assertTrue(isinstance(obj, pymonkey.Object))
411 self.assertFalse(isinstance(obj, pymonkey.Function))
412
413 def testObjectTypeCannotBeInstantiated(self):
414 self.assertRaises(TypeError, pymonkey.Object)
415
416 def testFunctionIsInstance(self):
417 def boop():
418 pass
419 obj = pymonkey.Runtime().new_context().new_function(boop, "boop")
420 self.assertTrue(isinstance(obj, pymonkey.Object))
421 self.assertTrue(isinstance(obj, pymonkey.Function))
422
423 def testFunctionTypeCannotBeInstantiated(self):
424 self.assertRaises(TypeError, pymonkey.Function)
425
426 def testObjectGetRuntimeWorks(self):
427 rt = pymonkey.Runtime()
428 obj = rt.new_context().new_object()
429 self.assertEqual(obj.get_runtime(), rt)
430
431 def testContextGetRuntimeWorks(self):
432 rt = pymonkey.Runtime()
433 cx = rt.new_context()
434 self.assertEqual(cx.get_runtime(), rt)
435
436 def testUndefinedCannotBeInstantiated(self):
437 self.assertRaises(TypeError, pymonkey.undefined)
438
439 def testEvaluateThrowsException(self):
440 cx = pymonkey.Runtime().new_context()
441 obj = cx.new_object()
442 self.assertRaises(pymonkey.error,
443 cx.evaluate_script,
444 obj, 'hai2u()', '<string>', 1)
445 self.assertEqual(self._tostring(cx,
446 self.last_exception.args[0]),
447 'ReferenceError: hai2u is not defined')
448
449 def testEvaluateReturnsUndefined(self):
450 retval = self._evaljs("")
451 self.assertTrue(retval is pymonkey.undefined)
452
453 def testEvaludateReturnsUnicodeWithEmbeddedNULs(self):
454 retval = self._evaljs("'\x00hi'")
455 self.assertEqual(retval, u'\x00hi')
456
457 def testEvaluateReturnsSMPUnicode(self):
458 # This is 'LINEAR B SYLLABLE B008 A', in the supplementary
459 # multilingual plane (SMP).
460 retval = self._evaljs("'\uD800\uDC00'")
461 self.assertEqual(retval, u'\U00010000')
462 self.assertEqual(retval.encode('utf-16'),
463 '\xff\xfe\x00\xd8\x00\xdc')
464
465 def testEvaluateReturnsBMPUnicode(self):
466 retval = self._evaljs("'o hai\u2026'")
467 self.assertTrue(type(retval) == unicode)
468 self.assertEqual(retval, u'o hai\u2026')
469
470 def testEvaluateReturnsObject(self):
471 cx = pymonkey.Runtime().new_context()
472 obj = cx.new_object()
473 cx.init_standard_classes(obj)
474 obj = cx.evaluate_script(obj, '({boop: 1})', '<string>', 1)
475 self.assertTrue(isinstance(obj, pymonkey.Object))
476 self.assertEqual(cx.get_property(obj, u"boop"), 1)
477
478 def testEvaluateReturnsFunction(self):
479 cx = pymonkey.Runtime().new_context()
480 obj = cx.new_object()
481 cx.init_standard_classes(obj)
482 obj = cx.evaluate_script(obj, '(function boop() { return 1; })',
483 '<string>', 1)
484 self.assertTrue(isinstance(obj, pymonkey.Function))
485
486 def testJsExceptionStateIsClearedAfterExceptionIsCaught(self):
487 cx = pymonkey.Runtime().new_context()
488 obj = cx.new_object()
489 self.assertRaises(pymonkey.error,
490 cx.evaluate_script,
491 obj, 'blah()', '<string>', 1)
492 self.assertEqual(cx.evaluate_script(obj, '5+3', '<string>', 1),
493 8)
494
495 def testCallFunctionRaisesErrorOnBadFuncArgs(self):
496 cx = pymonkey.Runtime().new_context()
497 obj = cx.new_object()
498 obj = cx.evaluate_script(
499 obj,
500 '(function boop(a, b) { return a+b+this.c; })',
501 '<string>', 1
502 )
503 self.assertRaises(
504 NotImplementedError,
505 cx.call_function,
506 obj, obj, (1, self)
507 )
508
509 def _tostring(self, cx, obj):
510 return cx.call_function(obj,
511 cx.get_property(obj, u"toString"),
512 ())
513
514 def testCallFunctionRaisesErrorFromJS(self):
515 cx = pymonkey.Runtime().new_context()
516 obj = cx.new_object()
517 obj = cx.evaluate_script(
518 obj,
519 '(function boop(a, b) { blarg(); })',
520 '<string>', 1
521 )
522 self.assertRaises(pymonkey.error,
523 cx.call_function,
524 obj, obj, (1,))
525 self.assertEqual(self._tostring(cx,
526 self.last_exception.args[0]),
527 'ReferenceError: blarg is not defined')
528
529 def testInitStandardClassesRaisesExcOnRuntimeMismatch(self):
530 cx2 = pymonkey.Runtime().new_context()
531 cx = pymonkey.Runtime().new_context()
532 obj = cx.new_object()
533 self.assertRaises(ValueError,
534 cx2.init_standard_classes,
535 obj)
536 self.assertEqual(self.last_exception.args[0],
537 'JS runtime mismatch')
538
539 def testCallFunctionWorks(self):
540 cx = pymonkey.Runtime().new_context()
541 obj = cx.new_object()
542 thisArg = cx.new_object()
543 cx.define_property(thisArg, "c", 3)
544 cx.init_standard_classes(obj)
545 obj = cx.evaluate_script(
546 obj,
547 '(function boop(a, b) { return a+b+this.c; })',
548 '<string>', 1
549 )
550 self.assertEqual(cx.call_function(thisArg, obj, (1,2)), 6)
551
552 def testEvaluateReturnsTrue(self):
553 self.assertTrue(self._evaljs('true') is True)
554
555 def testEvaluateReturnsFalse(self):
556 self.assertTrue(self._evaljs('false') is False)
557
558 def testEvaluateReturnsNone(self):
559 self.assertTrue(self._evaljs('null') is None)
560
561 def testEvaluateReturnsIntegers(self):
562 self.assertEqual(self._evaljs('1+3'), 4)
563
564 def testEvaluateReturnsNegativeIntegers(self):
565 self.assertEqual(self._evaljs('-5'), -5)
566
567 def testEvaluateReturnsBigIntegers(self):
568 self.assertEqual(self._evaljs('2147483647*2'),
569 2147483647*2)
570
571 def testEvaluateReturnsFloats(self):
572 self.assertEqual(self._evaljs('1.1+3'), 4.1)
573
574 if __name__ == '__main__':
575 unittest.main()