foundation_tests.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. """Tests for cement.core.setup."""
  2. import os
  3. import sys
  4. import json
  5. import signal
  6. from time import sleep
  7. from cement.core import foundation, exc, backend, config, extension, plugin
  8. from cement.core.handler import CementBaseHandler
  9. from cement.core.controller import CementBaseController, expose
  10. from cement.core import log, output, hook, arg, controller
  11. from cement.core.interface import Interface
  12. from cement.utils import test
  13. from cement.core.exc import CaughtSignal
  14. from cement.utils.misc import init_defaults, rando, minimal_logger
  15. from nose.plugins.attrib import attr
  16. APP = rando()[:12]
  17. def my_extended_func():
  18. return 'KAPLA'
  19. class DeprecatedApp(foundation.CementApp):
  20. class Meta:
  21. label = 'deprecated'
  22. defaults = None
  23. class HookTestException(Exception):
  24. pass
  25. class MyTestInterface(Interface):
  26. class IMeta:
  27. label = 'my_test_interface'
  28. class MyTestHandler(CementBaseHandler):
  29. class Meta:
  30. label = 'my_test_handler'
  31. interface = MyTestInterface
  32. class TestOutputHandler(output.CementOutputHandler):
  33. file_suffix = None
  34. class Meta:
  35. interface = output.IOutput
  36. label = 'test_output_handler'
  37. def _setup(self, config_obj):
  38. self.config = config_obj
  39. def render(self, data_dict, template=None):
  40. return None
  41. class BogusBaseController(controller.CementBaseController):
  42. class Meta:
  43. label = 'bad_base_controller_label'
  44. def my_hook_one(app):
  45. return 1
  46. def my_hook_two(app):
  47. return 2
  48. def my_hook_three(app):
  49. return 3
  50. class FoundationTestCase(test.CementCoreTestCase):
  51. def setUp(self):
  52. super(FoundationTestCase, self).setUp()
  53. self.app = self.make_app('my_app')
  54. def test_argv_is_none(self):
  55. app = self.make_app(APP, argv=None)
  56. app.setup()
  57. self.eq(app.argv, list(sys.argv[1:]))
  58. def test_framework_logging_is_true(self):
  59. del os.environ['CEMENT_FRAMEWORK_LOGGING']
  60. app = self.make_app(APP, argv=None, framework_logging=True)
  61. app.setup()
  62. self.eq(os.environ['CEMENT_FRAMEWORK_LOGGING'], '1')
  63. ml = minimal_logger(__name__)
  64. self.eq(ml.logging_is_enabled, True)
  65. def test_framework_logging_is_false(self):
  66. del os.environ['CEMENT_FRAMEWORK_LOGGING']
  67. app = self.make_app(APP, argv=None, framework_logging=False)
  68. app.setup()
  69. self.eq(os.environ['CEMENT_FRAMEWORK_LOGGING'], '0')
  70. ml = minimal_logger(__name__)
  71. self.eq(ml.logging_is_enabled, False)
  72. # coverage... should default to True if no key in os.environ
  73. del os.environ['CEMENT_FRAMEWORK_LOGGING']
  74. self.eq(ml.logging_is_enabled, True)
  75. def test_bootstrap(self):
  76. app = self.make_app('my_app', bootstrap='tests.bootstrap')
  77. app.setup()
  78. self.eq(app._loaded_bootstrap.__name__, 'tests.bootstrap')
  79. def test_reload_bootstrap(self):
  80. app = self.make_app('my_app', bootstrap='cement.utils.test')
  81. app._loaded_bootstrap = test
  82. app.setup()
  83. self.eq(app._loaded_bootstrap.__name__, 'cement.utils.test')
  84. def test_argv(self):
  85. app = self.make_app('my_app', argv=['bogus', 'args'])
  86. self.eq(app.argv, ['bogus', 'args'])
  87. @test.raises(exc.FrameworkError)
  88. def test_resolve_handler_bad_handler(self):
  89. class Bogus(object):
  90. pass
  91. try:
  92. self.app._resolve_handler('output', Bogus)
  93. except exc.FrameworkError as e:
  94. self.ok(e.msg.find('resolve'))
  95. raise
  96. def test_default(self):
  97. self.app.setup()
  98. self.app.run()
  99. def test_passed_handlers(self):
  100. from cement.ext import ext_configparser
  101. from cement.ext import ext_logging
  102. from cement.ext import ext_argparse
  103. from cement.ext import ext_plugin
  104. from cement.ext import ext_dummy
  105. # forces CementApp._resolve_handler to register the handler
  106. from cement.ext import ext_json
  107. app = self.make_app('my-app-test',
  108. config_handler=ext_configparser.ConfigParserConfigHandler,
  109. log_handler=ext_logging.LoggingLogHandler(),
  110. arg_handler=ext_argparse.ArgParseArgumentHandler(),
  111. extension_handler=extension.CementExtensionHandler(),
  112. plugin_handler=ext_plugin.CementPluginHandler(),
  113. output_handler=ext_json.JsonOutputHandler(),
  114. mail_handler=ext_dummy.DummyMailHandler(),
  115. argv=[__file__, '--debug']
  116. )
  117. app.setup()
  118. def test_debug(self):
  119. app = self.make_app('my-app-test', argv=[__file__])
  120. app.setup()
  121. self.eq(app.debug, False)
  122. self.reset_backend()
  123. app = self.make_app('my-app-test', argv=[__file__, '--debug'])
  124. app.setup()
  125. self.eq(app.debug, True)
  126. self.reset_backend()
  127. defaults = init_defaults('my-app-test')
  128. defaults['my-app-test']['debug'] = True
  129. app = self.make_app('my-app-test', argv=[__file__],
  130. config_defaults=defaults)
  131. app.setup()
  132. self.eq(app.debug, True)
  133. def test_render(self):
  134. # Render with default
  135. self.app.setup()
  136. self.app.render(dict(foo='bar'))
  137. # Render with no output_handler... this is hackish, but there are
  138. # circumstances where app.output would be None.
  139. app = self.make_app('test', output_handler=None)
  140. app.setup()
  141. app.output = None
  142. app.render(dict(foo='bar'))
  143. def test_render_out_to_file(self):
  144. self.app = self.make_app(APP, extensions=['json'],
  145. output_handler='json')
  146. self.app.setup()
  147. self.app.run()
  148. f = open(self.tmp_file, 'w')
  149. self.app.render(dict(foo='bar'), out=f)
  150. f.close()
  151. f = open(self.tmp_file, 'r')
  152. data = json.load(f)
  153. f.close()
  154. self.eq(data, dict(foo='bar'))
  155. @test.raises(TypeError)
  156. def test_render_bad_out(self):
  157. self.app.setup()
  158. self.app.run()
  159. try:
  160. self.app.render(dict(foo='bar'), out='bogus type')
  161. except TypeError as e:
  162. self.eq(e.args[0], "Argument 'out' must be a 'file' like object")
  163. raise
  164. @test.raises(exc.FrameworkError)
  165. def test_bad_label(self):
  166. try:
  167. app = foundation.CementApp(None)
  168. except exc.FrameworkError as e:
  169. # FIX ME: verify error msg
  170. raise
  171. @test.raises(exc.FrameworkError)
  172. def test_bad_label_chars(self):
  173. try:
  174. app = foundation.CementApp('some!bogus()label')
  175. except exc.FrameworkError as e:
  176. self.ok(e.msg.find('alpha-numeric'))
  177. raise
  178. def test_add_arg_shortcut(self):
  179. self.app.setup()
  180. self.app.add_arg('--foo', action='store')
  181. def test_reset_output_handler(self):
  182. app = self.make_app('test', argv=[], output_handler=TestOutputHandler)
  183. app.setup()
  184. app.run()
  185. app.output = None
  186. app._meta.output_handler = None
  187. app._setup_output_handler()
  188. def test_lay_cement(self):
  189. app = self.make_app('test', argv=['--quiet'])
  190. def test_none_member(self):
  191. class Test(object):
  192. var = None
  193. self.app.setup()
  194. self.app.args.parsed_args = Test()
  195. try:
  196. self.app._parse_args()
  197. except SystemExit:
  198. pass
  199. @test.raises(exc.CaughtSignal)
  200. def test_cement_signal_handler(self):
  201. import signal
  202. import types
  203. global app
  204. app = self.make_app('test')
  205. frame = sys._getframe(0)
  206. try:
  207. foundation.cement_signal_handler(signal.SIGTERM, frame)
  208. except exc.CaughtSignal as e:
  209. self.eq(e.signum, signal.SIGTERM)
  210. self.ok(isinstance(e.frame, types.FrameType))
  211. raise
  212. def test_cement_without_signals(self):
  213. app = self.make_app('test', catch_signals=None)
  214. app.setup()
  215. def test_extend(self):
  216. self.app.extend('kapla', my_extended_func)
  217. self.eq(self.app.kapla(), 'KAPLA')
  218. @test.raises(exc.FrameworkError)
  219. def test_extended_duplicate(self):
  220. self.app.extend('config', my_extended_func)
  221. def test_no_handler(self):
  222. app = self.make_app(APP)
  223. app._resolve_handler('cache', None, raise_error=False)
  224. def test_config_files_is_none(self):
  225. app = self.make_app(APP, config_files=None)
  226. app.setup()
  227. label = APP
  228. user_home = os.path.abspath(os.path.expanduser(os.environ['HOME']))
  229. files = [
  230. os.path.join('/', 'etc', label, '%s.conf' % label),
  231. os.path.join(user_home, '.%s.conf' % label),
  232. os.path.join(user_home, '.%s' % label, 'config'),
  233. ]
  234. for f in files:
  235. res = f in app._meta.config_files
  236. self.ok(res)
  237. @test.raises(exc.FrameworkError)
  238. def test_base_controller_label(self):
  239. app = self.make_app(APP, base_controller=BogusBaseController)
  240. app.setup()
  241. def test_pargs(self):
  242. app = self.make_app(argv=['--debug'])
  243. app.setup()
  244. app.run()
  245. self.eq(app.pargs.debug, True)
  246. def test_last_rendered(self):
  247. self.app.setup()
  248. output_text = self.app.render({'foo': 'bar'})
  249. last_data, last_output = self.app.last_rendered
  250. self.eq({'foo': 'bar'}, last_data)
  251. self.eq(output_text, last_output)
  252. def test_get_last_rendered(self):
  253. # DEPRECATED - REMOVE AFTER THE FUNCTION IS REMOVED
  254. self.app.setup()
  255. output_text = self.app.render({'foo': 'bar'})
  256. last_data, last_output = self.app.get_last_rendered()
  257. self.eq({'foo': 'bar'}, last_data)
  258. self.eq(output_text, last_output)
  259. def test_with_operator(self):
  260. with self.app_class() as app:
  261. app.run()
  262. @test.raises(SystemExit)
  263. def test_close_with_code(self):
  264. app = self.make_app(APP, exit_on_close=True)
  265. app.setup()
  266. app.run()
  267. try:
  268. app.close(114)
  269. except SystemExit as e:
  270. self.eq(e.code, 114)
  271. raise
  272. @test.raises(AssertionError)
  273. def test_close_with_bad_code(self):
  274. self.app.setup()
  275. self.app.run()
  276. try:
  277. self.app.close('Not An Int')
  278. except AssertionError as e:
  279. self.eq(e.args[0], "Invalid exit status code (must be integer)")
  280. raise
  281. def test_handler_override_options(self):
  282. app = self.make_app(APP,
  283. argv=['-o', 'json'],
  284. extensions=['yaml', 'json'],
  285. )
  286. app.setup()
  287. app.run()
  288. self.eq(app._meta.output_handler, 'json')
  289. def test_handler_override_options_is_none(self):
  290. app = self.make_app(APP,
  291. core_handler_override_options=None,
  292. handler_override_options=None
  293. )
  294. app.setup()
  295. app.run()
  296. def test_handler_override_invalid_interface(self):
  297. app = self.make_app(APP,
  298. handler_override_options=dict(
  299. bogus_interface=(['-f'], ['--foo'], {}),
  300. )
  301. )
  302. app.setup()
  303. app.run()
  304. def test_handler_override_options_not_passed(self):
  305. app = self.make_app(APP,
  306. extensions=['yaml', 'json'],
  307. )
  308. app.setup()
  309. app.run()
  310. def test_suppress_output_while_debug(self):
  311. app = self.make_app(APP, debug=True)
  312. app.setup()
  313. app._suppress_output()
  314. def test_core_meta_override(self):
  315. defaults = init_defaults(APP)
  316. defaults[APP]['mail_handler'] = 'dummy'
  317. app = self.make_app(APP, debug=True, config_defaults=defaults)
  318. app.setup()
  319. app.run()
  320. def test_define_hooks_meta(self):
  321. app = self.make_app(APP, define_hooks=['my_custom_hook'])
  322. app.setup()
  323. self.ok(hook.defined('my_custom_hook'))
  324. @test.raises(HookTestException)
  325. def test_register_hooks_meta(self):
  326. def my_custom_hook_func():
  327. raise HookTestException('OK')
  328. app = self.make_app(APP,
  329. define_hooks=['my_custom_hook'],
  330. hooks=[('my_custom_hook', my_custom_hook_func)])
  331. app.setup()
  332. for res in hook.run('my_custom_hook'):
  333. pass
  334. def test_define_handlers_meta(self):
  335. app = self.make_app(APP, define_handlers=[MyTestInterface])
  336. app.setup()
  337. self.ok(app.handler.defined('my_test_interface'))
  338. def test_register_handlers_meta(self):
  339. app = self.make_app(APP,
  340. define_handlers=[MyTestInterface],
  341. handlers=[MyTestHandler],
  342. )
  343. app.setup()
  344. self.ok(app.handler.registered('my_test_interface',
  345. 'my_test_handler'))
  346. def test_disable_backend_globals(self):
  347. app = self.make_app(APP,
  348. use_backend_globals=False,
  349. define_handlers=[MyTestInterface],
  350. handlers=[MyTestHandler],
  351. define_hooks=['my_hook'],
  352. )
  353. app.setup()
  354. self.ok(app.handler.registered('my_test_interface',
  355. 'my_test_handler'))
  356. self.ok(app.hook.defined('my_hook'))
  357. def test_reload(self):
  358. with self.app as app:
  359. app.hook.define('bogus_hook1')
  360. app.handler.define(MyTestInterface)
  361. app.run()
  362. self.ok(app.hook.defined('bogus_hook1'))
  363. self.ok(app.handler.defined('my_test_interface'))
  364. app.reload()
  365. self.eq(app.hook.defined('bogus_hook1'), False)
  366. self.eq(app.handler.defined('my_test_interface'), False)
  367. app.run()
  368. @test.raises(AssertionError)
  369. def test_run_forever(self):
  370. class Controller(CementBaseController):
  371. class Meta:
  372. label = 'base'
  373. @expose()
  374. def runit(self):
  375. raise Exception("Fake some error")
  376. app = self.make_app(base_controller=Controller, argv=['runit'])
  377. def handler(signum, frame):
  378. raise AssertionError('It ran forever!')
  379. # set the signal handler and a 5-second alarm
  380. signal.signal(signal.SIGALRM, handler)
  381. signal.alarm(5)
  382. try:
  383. # this will run indefinitely
  384. with app as app:
  385. app.run_forever()
  386. except AssertionError as e:
  387. self.eq(e.args[0], 'It ran forever!')
  388. raise
  389. finally:
  390. signal.alarm(0)