/johnny/tests/base.py

https://bitbucket.org/jmoiron/johnny-cache/ · Python · 145 lines · 122 code · 15 blank · 8 comment · 12 complexity · 02fc9201a8cd820b31f6914319384057 MD5 · raw file

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """Base test class for Johnny Cache Tests."""
  4. import sys
  5. import django
  6. from django.test import TestCase, TransactionTestCase
  7. from django.conf import settings
  8. from django.core.management import call_command
  9. from django.db.models.loading import load_app
  10. from johnny import settings as johnny_settings
  11. from johnny.decorators import wraps, available_attrs
  12. # order matters here; I guess we aren't deferring foreign key checking :\
  13. johnny_fixtures = ['authors.json', 'genres.json', 'publishers.json', 'books.json']
  14. def show_johnny_signals(hit=None, miss=None):
  15. """A decorator that can be put on a test function that will show the
  16. johnny hit/miss signals by default, or do what is provided by the hit
  17. and miss keyword args."""
  18. from pprint import pformat
  19. def _hit(*args, **kwargs):
  20. print "hit:\n\t%s\n\t%s\n" % (pformat(args), pformat(kwargs))
  21. def _miss(*args, **kwargs):
  22. print "miss:\n\t%s\n\t%s\n" % (pformat(args), pformat(kwargs))
  23. hit = hit or _hit
  24. miss = miss or _miss
  25. def deco(func):
  26. @wraps(func, assigned=available_attrs(func))
  27. def wrapped(*args, **kwargs):
  28. from johnny.signals import qc_hit, qc_miss
  29. qc_hit.connect(hit)
  30. qc_miss.connect(miss)
  31. try:
  32. ret = func(*args, **kwargs)
  33. finally:
  34. qc_hit.disconnect(hit)
  35. qc_miss.disconnect(miss)
  36. return ret
  37. return wrapped
  38. return deco
  39. def _pre_setup(self):
  40. self.saved_INSTALLED_APPS = settings.INSTALLED_APPS
  41. self.saved_DEBUG = settings.DEBUG
  42. test_app = 'johnny.tests.testapp'
  43. settings.INSTALLED_APPS = tuple(
  44. list(self.saved_INSTALLED_APPS) + [test_app]
  45. )
  46. settings.DEBUG = True
  47. # load our fake application and syncdb
  48. load_app(test_app)
  49. call_command('syncdb', verbosity=0, interactive=False)
  50. if hasattr(settings, 'DATABASES'):
  51. for dbname in settings.DATABASES:
  52. if dbname != 'default':
  53. call_command('syncdb', verbosity=0, interactive=False, database=dbname)
  54. def _post_teardown(self):
  55. settings.INSTALLED_APPS = self.saved_INSTALLED_APPS
  56. settings.DEBUG = self.saved_DEBUG
  57. class JohnnyTestCase(TestCase):
  58. def _pre_setup(self):
  59. _pre_setup(self)
  60. super(JohnnyTestCase, self)._pre_setup()
  61. def _post_teardown(self):
  62. _post_teardown(self)
  63. super(JohnnyTestCase, self)._post_teardown()
  64. class TransactionJohnnyTestCase(TransactionTestCase):
  65. def _pre_setup(self):
  66. _pre_setup(self)
  67. super(TransactionJohnnyTestCase, self)._pre_setup()
  68. def _post_teardown(self):
  69. _post_teardown(self)
  70. super(TransactionJohnnyTestCase, self)._post_teardown()
  71. class TransactionJohnnyWebTestCase(TransactionJohnnyTestCase):
  72. def _pre_setup(self):
  73. from johnny import middleware
  74. self.saved_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
  75. if getattr(self.__class__, 'middleware', None):
  76. settings.MIDDLEWARE_CLASSES = self.__class__.middleware
  77. self.saved_DISABLE_SETTING = getattr(johnny_settings, 'DISABLE_QUERYSET_CACHE', False)
  78. johnny_settings.DISABLE_QUERYSET_CACHE = False
  79. self.saved_TEMPLATE_LOADERS = settings.TEMPLATE_LOADERS
  80. if django.VERSION[:2] < (1, 3):
  81. if 'django.template.loaders.app_directories.load_template_source' not in settings.TEMPLATE_LOADERS:
  82. settings.TEMPLATE_LOADERS += ('django.template.loaders.app_directories.load_template_source',)
  83. else:
  84. if 'django.template.loaders.app_directories.Loader' not in settings.TEMPLATE_LOADERS:
  85. settings.TEMPLATE_LOADERS += ('django.template.loaders.app_directories.Loader',)
  86. self.middleware = middleware.QueryCacheMiddleware()
  87. self.saved_ROOT_URLCONF = settings.ROOT_URLCONF
  88. settings.ROOT_URLCONF = 'johnny.tests.testapp.urls'
  89. super(TransactionJohnnyWebTestCase, self)._pre_setup()
  90. def _post_teardown(self):
  91. self.middleware.unpatch()
  92. johnny_settings.DISABLE_QUERYSET_CACHE = self.saved_DISABLE_SETTING
  93. settings.MIDDLEWARE_CLASSES = self.saved_MIDDLEWARE_CLASSES
  94. settings.ROOT_URLCONF = self.saved_ROOT_URLCONF
  95. settings.TEMPLATE_LOADERS = self.saved_TEMPLATE_LOADERS
  96. super(TransactionJohnnyWebTestCase, self)._post_teardown()
  97. class message_queue(object):
  98. """Return a message queue that gets 'hit' or 'miss' messages. The signal
  99. handlers use weakrefs, so if we don't save references to this object they
  100. will get gc'd pretty fast."""
  101. def __init__(self):
  102. from johnny.signals import qc_hit, qc_miss
  103. from Queue import Queue as queue
  104. self.q = queue()
  105. qc_hit.connect(self._hit)
  106. qc_miss.connect(self._miss)
  107. def _hit(self, *a, **k): self.q.put(True)
  108. def _miss(self, *a, **k): self.q.put(False)
  109. def clear(self):
  110. while not self.q.empty():
  111. self.q.get_nowait()
  112. def get(self): return self.q.get()
  113. def get_nowait(self): return self.q.get_nowait()
  114. def qsize(self): return self.q.qsize()
  115. def empty(self): return self.q.empty()
  116. def supports_transactions(con):
  117. """A convenience function which will work across multiple django versions
  118. that checks whether or not a connection supports transactions."""
  119. features = con.features.__dict__
  120. vendor = con.vendor
  121. if features.get("supports_transactions", False):
  122. if vendor == "mysql" and not features.get('_storage_engine', '') == "InnoDB":
  123. print "MySQL connection reports transactions supported but storage engine != InnoDB."
  124. return False
  125. return True
  126. return False