PageRenderTime 36ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/conftest.py

https://bitbucket.org/varialus/jyjy
Python | 580 lines | 550 code | 10 blank | 20 comment | 4 complexity | a228322ab9f9bdcb95c329273d2827b2 MD5 | raw file
  1. import py, pytest, sys, os, textwrap, types
  2. from pypy.interpreter.gateway import app2interp_temp
  3. from pypy.interpreter.error import OperationError
  4. from pypy.interpreter.function import Method
  5. from pypy.tool.pytest import appsupport
  6. from pypy.tool.option import make_config, make_objspace
  7. from pypy.config.config import ConflictConfigError
  8. from inspect import isclass, getmro
  9. from pypy.tool.udir import udir
  10. from pypy.tool.autopath import pypydir
  11. from pypy.tool import leakfinder
  12. # pytest settings
  13. rsyncdirs = ['.', '../lib-python', '../lib_pypy', '../demo']
  14. rsyncignore = ['_cache']
  15. # PyPy's command line extra options (these are added
  16. # to py.test's standard options)
  17. #
  18. option = None
  19. def pytest_report_header():
  20. return "pytest-%s from %s" %(pytest.__version__, pytest.__file__)
  21. def pytest_configure(config):
  22. global option
  23. option = config.option
  24. def _set_platform(opt, opt_str, value, parser):
  25. from pypy.config.translationoption import PLATFORMS
  26. from pypy.translator.platform import set_platform
  27. if value not in PLATFORMS:
  28. raise ValueError("%s not in %s" % (value, PLATFORMS))
  29. set_platform(value, None)
  30. def pytest_addoption(parser):
  31. group = parser.getgroup("pypy options")
  32. group.addoption('--view', action="store_true", dest="view", default=False,
  33. help="view translation tests' flow graphs with Pygame")
  34. group.addoption('-A', '--runappdirect', action="store_true",
  35. default=False, dest="runappdirect",
  36. help="run applevel tests directly on python interpreter (not through PyPy)")
  37. group.addoption('--direct', action="store_true",
  38. default=False, dest="rundirect",
  39. help="run pexpect tests directly")
  40. group.addoption('-P', '--platform', action="callback", type="string",
  41. default="host", callback=_set_platform,
  42. help="set up tests to use specified platform as compile/run target")
  43. group = parser.getgroup("JIT options")
  44. group.addoption('--viewloops', action="store_true",
  45. default=False, dest="viewloops",
  46. help="show only the compiled loops")
  47. def pytest_sessionstart():
  48. # have python subprocesses avoid startup customizations by default
  49. try:
  50. del os.environ['PYTHONSTARTUP']
  51. except KeyError:
  52. pass
  53. def pytest_funcarg__space(request):
  54. spaceconfig = getattr(request.cls, 'spaceconfig', {})
  55. return gettestobjspace(**spaceconfig)
  56. _SPACECACHE={}
  57. def gettestobjspace(name=None, **kwds):
  58. """ helper for instantiating and caching space's for testing.
  59. """
  60. try:
  61. config = make_config(option, objspace=name, **kwds)
  62. except ConflictConfigError, e:
  63. # this exception is typically only raised if a module is not available.
  64. # in this case the test should be skipped
  65. py.test.skip(str(e))
  66. key = config.getkey()
  67. try:
  68. return _SPACECACHE[key]
  69. except KeyError:
  70. if getattr(option, 'runappdirect', None):
  71. if name not in (None, 'std'):
  72. myname = getattr(sys, 'pypy_objspaceclass', '')
  73. if not myname.lower().startswith(name):
  74. py.test.skip("cannot runappdirect test: "
  75. "%s objspace required" % (name,))
  76. return TinyObjSpace(**kwds)
  77. space = maketestobjspace(config)
  78. _SPACECACHE[key] = space
  79. return space
  80. def maketestobjspace(config=None):
  81. if config is None:
  82. config = make_config(option)
  83. try:
  84. space = make_objspace(config)
  85. except OperationError, e:
  86. check_keyboard_interrupt(e)
  87. if option.verbose:
  88. import traceback
  89. traceback.print_exc()
  90. py.test.fail("fatal: cannot initialize objspace: %r" %
  91. (config.objspace.name,))
  92. space.startup() # Initialize all builtin modules
  93. space.setitem(space.builtin.w_dict, space.wrap('AssertionError'),
  94. appsupport.build_pytest_assertion(space))
  95. space.setitem(space.builtin.w_dict, space.wrap('raises'),
  96. space.wrap(appsupport.app_raises))
  97. space.setitem(space.builtin.w_dict, space.wrap('skip'),
  98. space.wrap(appsupport.app_skip))
  99. space.raises_w = appsupport.raises_w.__get__(space)
  100. space.eq_w = appsupport.eq_w.__get__(space)
  101. return space
  102. class TinyObjSpace(object):
  103. def __init__(self, **kwds):
  104. import sys
  105. info = getattr(sys, 'pypy_translation_info', None)
  106. for key, value in kwds.iteritems():
  107. if key == 'usemodules':
  108. if info is not None:
  109. for modname in value:
  110. ok = info.get('objspace.usemodules.%s' % modname,
  111. False)
  112. if not ok:
  113. py.test.skip("cannot runappdirect test: "
  114. "module %r required" % (modname,))
  115. else:
  116. if '__pypy__' in value:
  117. py.test.skip("no module __pypy__ on top of CPython")
  118. continue
  119. if info is None:
  120. py.test.skip("cannot runappdirect this test on top of CPython")
  121. has = info.get(key, None)
  122. if has != value:
  123. #print sys.pypy_translation_info
  124. py.test.skip("cannot runappdirect test: space needs %s = %s, "\
  125. "while pypy-c was built with %s" % (key, value, has))
  126. for name in ('int', 'long', 'str', 'unicode', 'None'):
  127. setattr(self, 'w_' + name, eval(name))
  128. def appexec(self, args, body):
  129. body = body.lstrip()
  130. assert body.startswith('(')
  131. src = py.code.Source("def anonymous" + body)
  132. d = {}
  133. exec src.compile() in d
  134. return d['anonymous'](*args)
  135. def wrap(self, obj):
  136. return obj
  137. def unpackiterable(self, itr):
  138. return list(itr)
  139. def is_true(self, obj):
  140. return bool(obj)
  141. def str_w(self, w_str):
  142. return w_str
  143. def newdict(self, module=None):
  144. return {}
  145. def newtuple(self, iterable):
  146. return tuple(iterable)
  147. def newlist(self, iterable):
  148. return list(iterable)
  149. def call_function(self, func, *args, **kwds):
  150. return func(*args, **kwds)
  151. def call_method(self, obj, name, *args, **kwds):
  152. return getattr(obj, name)(*args, **kwds)
  153. def getattr(self, obj, name):
  154. return getattr(obj, name)
  155. def setattr(self, obj, name, value):
  156. setattr(obj, name, value)
  157. def getbuiltinmodule(self, name):
  158. return __import__(name)
  159. def delslice(self, obj, *args):
  160. obj.__delslice__(*args)
  161. def translation_test_so_skip_if_appdirect():
  162. if option.runappdirect:
  163. py.test.skip("translation test, skipped for appdirect")
  164. class OpErrKeyboardInterrupt(KeyboardInterrupt):
  165. pass
  166. def check_keyboard_interrupt(e):
  167. # we cannot easily convert w_KeyboardInterrupt to KeyboardInterrupt
  168. # in general without a space -- here is an approximation
  169. try:
  170. if e.w_type.name == 'KeyboardInterrupt':
  171. tb = sys.exc_info()[2]
  172. raise OpErrKeyboardInterrupt, OpErrKeyboardInterrupt(), tb
  173. except AttributeError:
  174. pass
  175. #
  176. # Interfacing/Integrating with py.test's collection process
  177. #
  178. #
  179. def ensure_pytest_builtin_helpers(helpers='skip raises'.split()):
  180. """ hack (py.test.) raises and skip into builtins, needed
  181. for applevel tests to run directly on cpython but
  182. apparently earlier on "raises" was already added
  183. to module's globals.
  184. """
  185. import __builtin__
  186. for helper in helpers:
  187. if not hasattr(__builtin__, helper):
  188. setattr(__builtin__, helper, getattr(py.test, helper))
  189. def pytest_sessionstart(session):
  190. """ before session.main() is called. """
  191. # stick py.test raise in module globals -- carefully
  192. ensure_pytest_builtin_helpers()
  193. def pytest_pycollect_makemodule(path, parent):
  194. return PyPyModule(path, parent)
  195. class PyPyModule(py.test.collect.Module):
  196. """ we take care of collecting classes both at app level
  197. and at interp-level (because we need to stick a space
  198. at the class) ourselves.
  199. """
  200. def accept_regular_test(self):
  201. if self.config.option.runappdirect:
  202. # only collect regular tests if we are in an 'app_test' directory,
  203. # or in test_lib_pypy
  204. names = self.listnames()
  205. return "app_test" in names or "test_lib_pypy" in names
  206. else:
  207. return True
  208. def funcnamefilter(self, name):
  209. if name.startswith('test_'):
  210. return self.accept_regular_test()
  211. if name.startswith('app_test_'):
  212. return True
  213. return False
  214. def classnamefilter(self, name):
  215. if name.startswith('Test'):
  216. return self.accept_regular_test()
  217. if name.startswith('AppTest'):
  218. return True
  219. if name.startswith('ExpectTest'):
  220. return True
  221. #XXX todo
  222. #if name.startswith('AppExpectTest'):
  223. # return True
  224. return False
  225. def makeitem(self, name, obj):
  226. if isclass(obj) and self.classnamefilter(name):
  227. if name.startswith('AppTest'):
  228. return AppClassCollector(name, parent=self)
  229. elif name.startswith('ExpectTest'):
  230. if self.config.option.rundirect:
  231. return py.test.collect.Class(name, parent=self)
  232. return ExpectClassCollector(name, parent=self)
  233. # XXX todo
  234. #elif name.startswith('AppExpectTest'):
  235. # if option.rundirect:
  236. # return AppClassCollector(name, parent=self)
  237. # return AppExpectClassCollector(name, parent=self)
  238. else:
  239. return IntClassCollector(name, parent=self)
  240. elif hasattr(obj, 'func_code') and self.funcnamefilter(name):
  241. if name.startswith('app_test_'):
  242. assert not obj.func_code.co_flags & 32, \
  243. "generator app level functions? you must be joking"
  244. return AppTestFunction(name, parent=self)
  245. elif obj.func_code.co_flags & 32: # generator function
  246. return pytest.Generator(name, parent=self)
  247. else:
  248. return IntTestFunction(name, parent=self)
  249. def skip_on_missing_buildoption(**ropts):
  250. __tracebackhide__ = True
  251. import sys
  252. options = getattr(sys, 'pypy_translation_info', None)
  253. if options is None:
  254. py.test.skip("not running on translated pypy "
  255. "(btw, i would need options: %s)" %
  256. (ropts,))
  257. for opt in ropts:
  258. if not options.has_key(opt) or options[opt] != ropts[opt]:
  259. break
  260. else:
  261. return
  262. py.test.skip("need translated pypy with: %s, got %s"
  263. %(ropts,options))
  264. class LazyObjSpaceGetter(object):
  265. def __get__(self, obj, cls=None):
  266. space = gettestobjspace()
  267. if cls:
  268. cls.space = space
  269. return space
  270. class AppError(Exception):
  271. def __init__(self, excinfo):
  272. self.excinfo = excinfo
  273. def pytest_runtest_setup(__multicall__, item):
  274. if isinstance(item, py.test.collect.Function):
  275. appclass = item.getparent(PyPyClassCollector)
  276. if appclass is not None:
  277. spaceconfig = getattr(appclass.obj, 'spaceconfig', None)
  278. if spaceconfig:
  279. appclass.obj.space = gettestobjspace(**spaceconfig)
  280. __multicall__.execute()
  281. if isinstance(item, py.test.collect.Function):
  282. if not getattr(item.obj, 'dont_track_allocations', False):
  283. leakfinder.start_tracking_allocations()
  284. def pytest_runtest_call(__multicall__, item):
  285. __multicall__.execute()
  286. item._success = True
  287. def pytest_runtest_teardown(__multicall__, item):
  288. __multicall__.execute()
  289. if isinstance(item, py.test.collect.Function):
  290. if (not getattr(item.obj, 'dont_track_allocations', False)
  291. and leakfinder.TRACK_ALLOCATIONS):
  292. item._pypytest_leaks = leakfinder.stop_tracking_allocations(False)
  293. else: # stop_tracking_allocations() already called
  294. item._pypytest_leaks = None
  295. # check for leaks, but only if the test passed so far
  296. if getattr(item, '_success', False) and item._pypytest_leaks:
  297. raise leakfinder.MallocMismatch(item._pypytest_leaks)
  298. if 'pygame' in sys.modules:
  299. assert option.view, ("should not invoke Pygame "
  300. "if conftest.option.view is False")
  301. _pygame_imported = False
  302. class IntTestFunction(py.test.collect.Function):
  303. def __init__(self, *args, **kwargs):
  304. super(IntTestFunction, self).__init__(*args, **kwargs)
  305. self.keywords['interplevel'] = True
  306. def runtest(self):
  307. try:
  308. super(IntTestFunction, self).runtest()
  309. except OperationError, e:
  310. check_keyboard_interrupt(e)
  311. raise
  312. except Exception, e:
  313. cls = e.__class__
  314. while cls is not Exception:
  315. if cls.__name__ == 'DistutilsPlatformError':
  316. from distutils.errors import DistutilsPlatformError
  317. if isinstance(e, DistutilsPlatformError):
  318. py.test.skip('%s: %s' % (e.__class__.__name__, e))
  319. cls = cls.__bases__[0]
  320. raise
  321. class AppTestFunction(py.test.collect.Function):
  322. def __init__(self, *args, **kwargs):
  323. super(AppTestFunction, self).__init__(*args, **kwargs)
  324. self.keywords['applevel'] = True
  325. def _prunetraceback(self, traceback):
  326. return traceback
  327. def execute_appex(self, space, target, *args):
  328. try:
  329. target(*args)
  330. except OperationError, e:
  331. tb = sys.exc_info()[2]
  332. if e.match(space, space.w_KeyboardInterrupt):
  333. raise OpErrKeyboardInterrupt, OpErrKeyboardInterrupt(), tb
  334. appexcinfo = appsupport.AppExceptionInfo(space, e)
  335. if appexcinfo.traceback:
  336. raise AppError, AppError(appexcinfo), tb
  337. raise
  338. def runtest(self):
  339. target = self.obj
  340. if self.config.option.runappdirect:
  341. return target()
  342. space = gettestobjspace()
  343. filename = self._getdynfilename(target)
  344. func = app2interp_temp(target, filename=filename)
  345. print "executing", func
  346. self.execute_appex(space, func, space)
  347. def repr_failure(self, excinfo):
  348. if excinfo.errisinstance(AppError):
  349. excinfo = excinfo.value.excinfo
  350. return super(AppTestFunction, self).repr_failure(excinfo)
  351. def _getdynfilename(self, func):
  352. code = getattr(func, 'im_func', func).func_code
  353. return "[%s:%s]" % (code.co_filename, code.co_firstlineno)
  354. class AppTestMethod(AppTestFunction):
  355. def setup(self):
  356. super(AppTestMethod, self).setup()
  357. instance = self.parent.obj
  358. w_instance = self.parent.w_instance
  359. space = instance.space
  360. for name in dir(instance):
  361. if name.startswith('w_'):
  362. if self.config.option.runappdirect:
  363. setattr(instance, name[2:], getattr(instance, name))
  364. else:
  365. obj = getattr(instance, name)
  366. if isinstance(obj, types.MethodType):
  367. source = py.std.inspect.getsource(obj).lstrip()
  368. w_func = space.appexec([], textwrap.dedent("""
  369. ():
  370. %s
  371. return %s
  372. """) % (source, name))
  373. w_obj = Method(space, w_func, w_instance, space.w_None)
  374. else:
  375. w_obj = obj
  376. space.setattr(w_instance, space.wrap(name[2:]), w_obj)
  377. def runtest(self):
  378. target = self.obj
  379. if self.config.option.runappdirect:
  380. return target()
  381. space = target.im_self.space
  382. filename = self._getdynfilename(target)
  383. func = app2interp_temp(target.im_func, filename=filename)
  384. w_instance = self.parent.w_instance
  385. self.execute_appex(space, func, space, w_instance)
  386. class PyPyClassCollector(py.test.collect.Class):
  387. def setup(self):
  388. cls = self.obj
  389. if not hasattr(cls, 'spaceconfig'):
  390. cls.space = LazyObjSpaceGetter()
  391. else:
  392. assert hasattr(cls, 'space') # set by pytest_runtest_setup
  393. super(PyPyClassCollector, self).setup()
  394. class IntInstanceCollector(py.test.collect.Instance):
  395. Function = IntTestFunction
  396. class IntClassCollector(PyPyClassCollector):
  397. Instance = IntInstanceCollector
  398. def _haskeyword(self, keyword):
  399. return keyword == 'interplevel' or \
  400. super(IntClassCollector, self)._haskeyword(keyword)
  401. def _keywords(self):
  402. return super(IntClassCollector, self)._keywords() + ['interplevel']
  403. class AppClassInstance(py.test.collect.Instance):
  404. Function = AppTestMethod
  405. def setup(self):
  406. super(AppClassInstance, self).setup()
  407. instance = self.obj
  408. space = instance.space
  409. w_class = self.parent.w_class
  410. if self.config.option.runappdirect:
  411. self.w_instance = instance
  412. else:
  413. self.w_instance = space.call_function(w_class)
  414. class AppClassCollector(PyPyClassCollector):
  415. Instance = AppClassInstance
  416. def _haskeyword(self, keyword):
  417. return keyword == 'applevel' or \
  418. super(AppClassCollector, self)._haskeyword(keyword)
  419. def _keywords(self):
  420. return super(AppClassCollector, self)._keywords() + ['applevel']
  421. def setup(self):
  422. super(AppClassCollector, self).setup()
  423. cls = self.obj
  424. #
  425. # <hack>
  426. for name in dir(cls):
  427. if name.startswith('test_'):
  428. func = getattr(cls, name, None)
  429. code = getattr(func, 'func_code', None)
  430. if code and code.co_flags & 32:
  431. raise AssertionError("unsupported: %r is a generator "
  432. "app-level test method" % (name,))
  433. # </hack>
  434. #
  435. space = cls.space
  436. clsname = cls.__name__
  437. if self.config.option.runappdirect:
  438. w_class = cls
  439. else:
  440. w_class = space.call_function(space.w_type,
  441. space.wrap(clsname),
  442. space.newtuple([]),
  443. space.newdict())
  444. self.w_class = w_class
  445. class ExpectTestMethod(py.test.collect.Function):
  446. def safe_name(target):
  447. s = "_".join(target)
  448. s = s.replace("()", "paren")
  449. s = s.replace(".py", "")
  450. s = s.replace(".", "_")
  451. s = s.replace(os.sep, "_")
  452. return s
  453. safe_name = staticmethod(safe_name)
  454. def safe_filename(self):
  455. name = self.safe_name(self.listnames())
  456. num = 0
  457. while udir.join(name + '.py').check():
  458. num += 1
  459. name = self.safe_name(self.listnames()) + "_" + str(num)
  460. return name + '.py'
  461. def _spawn(self, *args, **kwds):
  462. import pexpect
  463. child = pexpect.spawn(*args, **kwds)
  464. child.logfile = sys.stdout
  465. return child
  466. def spawn(self, argv):
  467. return self._spawn(sys.executable, argv)
  468. def runtest(self):
  469. target = self.obj
  470. import pexpect
  471. source = py.code.Source(target)[1:].deindent()
  472. filename = self.safe_filename()
  473. source.lines = ['import sys',
  474. 'sys.path.insert(0, %s)' % repr(os.path.dirname(pypydir))
  475. ] + source.lines
  476. source.lines.append('print "%s ok!"' % filename)
  477. f = udir.join(filename)
  478. f.write(source)
  479. # run target in the guarded environment
  480. child = self.spawn([str(f)])
  481. import re
  482. child.expect(re.escape(filename + " ok!"))
  483. class ExpectClassInstance(py.test.collect.Instance):
  484. Function = ExpectTestMethod
  485. class ExpectClassCollector(py.test.collect.Class):
  486. Instance = ExpectClassInstance
  487. def setup(self):
  488. super(ExpectClassCollector, self).setup()
  489. try:
  490. import pexpect
  491. except ImportError:
  492. py.test.skip("pexpect not found")
  493. def pytest_ignore_collect(path):
  494. return path.check(link=1)