diff --git a/.travis.yml b/.travis.yml index 839b3e1..e049309 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,11 @@ env: - DJANGO="Django>=1.4,<1.5" - DJANGO="Django>=1.5,<1.6" - DJANGO="Django>=1.6,<1.7" +- DJANGO="Django>=1.7,<1.8" +matrix: + exclude: + - python: "2.6" + env: DJANGO="Django>=1.7,<1.8" install: - pip install $DJANGO script: diff --git a/analytical/templatetags/clickmap.py b/analytical/templatetags/clickmap.py index 2d0414a..d0189d2 100644 --- a/analytical/templatetags/clickmap.py +++ b/analytical/templatetags/clickmap.py @@ -8,13 +8,13 @@ import re from django.template import Library, Node, TemplateSyntaxError -from analytical.utils import get_identity, is_internal_ip, disable_html, get_required_setting +from analytical.utils import is_internal_ip, disable_html, get_required_setting CLICKMAP_TRACKER_ID_RE = re.compile(r'^\d+$') TRACKING_CODE = """ """, rendered_tag) - @override_settings(INTERCOM_APP_ID=SETTING_DELETED) + @override_settings(INTERCOM_APP_ID=None) def test_no_account_number(self): self.assertRaises(AnalyticalException, IntercomNode) diff --git a/analytical/tests/test_tag_kiss_insights.py b/analytical/tests/test_tag_kiss_insights.py index 2626ac7..ea58250 100644 --- a/analytical/tests/test_tag_kiss_insights.py +++ b/analytical/tests/test_tag_kiss_insights.py @@ -4,9 +4,10 @@ Tests for the KISSinsights template tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.kiss_insights import KissInsightsNode -from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -25,11 +26,11 @@ class KissInsightsTagTestCase(TagTestCase): r = KissInsightsNode().render(Context()) self.assertTrue("//s3.amazonaws.com/ki.js/12345/abc.js" in r, r) - @override_settings(KISS_INSIGHTS_ACCOUNT_NUMBER=SETTING_DELETED) + @override_settings(KISS_INSIGHTS_ACCOUNT_NUMBER=None) def test_no_account_number(self): self.assertRaises(AnalyticalException, KissInsightsNode) - @override_settings(KISS_INSIGHTS_SITE_CODE=SETTING_DELETED) + @override_settings(KISS_INSIGHTS_SITE_CODE=None) def test_no_site_code(self): self.assertRaises(AnalyticalException, KissInsightsNode) diff --git a/analytical/tests/test_tag_kiss_metrics.py b/analytical/tests/test_tag_kiss_metrics.py index d520f52..b5cf2aa 100644 --- a/analytical/tests/test_tag_kiss_metrics.py +++ b/analytical/tests/test_tag_kiss_metrics.py @@ -5,9 +5,10 @@ Tests for the KISSmetrics tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.kiss_metrics import KissMetricsNode -from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -28,7 +29,7 @@ class KissMetricsTagTestCase(TagTestCase): self.assertTrue("//doug1izaerwt3.cloudfront.net/0123456789abcdef012345" "6789abcdef01234567.1.js" in r, r) - @override_settings(KISS_METRICS_API_KEY=SETTING_DELETED) + @override_settings(KISS_METRICS_API_KEY=None) def test_no_api_key(self): self.assertRaises(AnalyticalException, KissMetricsNode) diff --git a/analytical/tests/test_tag_mixpanel.py b/analytical/tests/test_tag_mixpanel.py index 3f61485..e5b1ffb 100644 --- a/analytical/tests/test_tag_mixpanel.py +++ b/analytical/tests/test_tag_mixpanel.py @@ -5,9 +5,10 @@ Tests for the Mixpanel tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.mixpanel import MixpanelNode -from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -29,7 +30,7 @@ class MixpanelTagTestCase(TagTestCase): "mixpanel.init('0123456789abcdef0123456789abcdef');" in r, r) - @override_settings(MIXPANEL_API_TOKEN=SETTING_DELETED) + @override_settings(MIXPANEL_API_TOKEN=None) def test_no_token(self): self.assertRaises(AnalyticalException, MixpanelNode) diff --git a/analytical/tests/test_tag_olark.py b/analytical/tests/test_tag_olark.py index bd93458..ecc3f56 100644 --- a/analytical/tests/test_tag_olark.py +++ b/analytical/tests/test_tag_olark.py @@ -4,9 +4,10 @@ Tests for the Olark template tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.olark import OlarkNode -from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -24,7 +25,7 @@ class OlarkTestCase(TagTestCase): r = OlarkNode().render(Context()) self.assertTrue("olark.identify('1234-567-89-0123');" in r, r) - @override_settings(OLARK_SITE_ID=SETTING_DELETED) + @override_settings(OLARK_SITE_ID=None) def test_no_site_id(self): self.assertRaises(AnalyticalException, OlarkNode) diff --git a/analytical/tests/test_tag_optimizely.py b/analytical/tests/test_tag_optimizely.py index 412eb46..a1cce26 100644 --- a/analytical/tests/test_tag_optimizely.py +++ b/analytical/tests/test_tag_optimizely.py @@ -4,9 +4,10 @@ Tests for the Optimizely template tags and filters. from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.optimizely import OptimizelyNode -from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -26,7 +27,7 @@ class OptimizelyTagTestCase(TagTestCase): '', OptimizelyNode().render(Context())) - @override_settings(OPTIMIZELY_ACCOUNT_NUMBER=SETTING_DELETED) + @override_settings(OPTIMIZELY_ACCOUNT_NUMBER=None) def test_no_account_number(self): self.assertRaises(AnalyticalException, OptimizelyNode) diff --git a/analytical/tests/test_tag_performable.py b/analytical/tests/test_tag_performable.py index 5e5c805..befee4b 100644 --- a/analytical/tests/test_tag_performable.py +++ b/analytical/tests/test_tag_performable.py @@ -5,9 +5,10 @@ Tests for the Performable template tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.performable import PerformableNode -from analytical.tests.utils import TagTestCase, override_settings, SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -25,7 +26,7 @@ class PerformableTagTestCase(TagTestCase): r = PerformableNode().render(Context()) self.assertTrue('/performable/pax/123ABC.js' in r, r) - @override_settings(PERFORMABLE_API_KEY=SETTING_DELETED) + @override_settings(PERFORMABLE_API_KEY=None) def test_no_api_key(self): self.assertRaises(AnalyticalException, PerformableNode) diff --git a/analytical/tests/test_tag_piwik.py b/analytical/tests/test_tag_piwik.py index e06a91c..35f6d54 100644 --- a/analytical/tests/test_tag_piwik.py +++ b/analytical/tests/test_tag_piwik.py @@ -4,10 +4,10 @@ Tests for the Piwik template tags and filters. from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.piwik import PiwikNode -from analytical.tests.utils import TagTestCase, override_settings, \ - SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -38,11 +38,11 @@ class PiwikTagTestCase(TagTestCase): self.assertTrue(' ? "https" : "http") + "://example.com/piwik/";' in r, r) - @override_settings(PIWIK_DOMAIN_PATH=SETTING_DELETED) + @override_settings(PIWIK_DOMAIN_PATH=None) def test_no_domain(self): self.assertRaises(AnalyticalException, PiwikNode) - @override_settings(PIWIK_SITE_ID=SETTING_DELETED) + @override_settings(PIWIK_SITE_ID=None) def test_no_siteid(self): self.assertRaises(AnalyticalException, PiwikNode) diff --git a/analytical/tests/test_tag_reinvigorate.py b/analytical/tests/test_tag_reinvigorate.py index f3c4111..c23febd 100644 --- a/analytical/tests/test_tag_reinvigorate.py +++ b/analytical/tests/test_tag_reinvigorate.py @@ -7,10 +7,10 @@ import re from django.contrib.auth.models import User, AnonymousUser from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.reinvigorate import ReinvigorateNode -from analytical.tests.utils import TagTestCase, override_settings, \ - SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -28,7 +28,7 @@ class ReinvigorateTagTestCase(TagTestCase): r = ReinvigorateNode().render(Context({})) self.assertTrue('reinvigorate.track("12345-abcdefghij");' in r, r) - @override_settings(REINVIGORATE_TRACKING_ID=SETTING_DELETED) + @override_settings(REINVIGORATE_TRACKING_ID=None) def test_no_tracking_id(self): self.assertRaises(AnalyticalException, ReinvigorateNode) diff --git a/analytical/tests/test_tag_snapengage.py b/analytical/tests/test_tag_snapengage.py index 931b1fb..07b96bc 100644 --- a/analytical/tests/test_tag_snapengage.py +++ b/analytical/tests/test_tag_snapengage.py @@ -4,14 +4,14 @@ Tests for the SnapEngage template tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.template import Context +from django.test.utils import override_settings from django.utils import translation from analytical.templatetags.snapengage import SnapEngageNode, \ BUTTON_STYLE_LIVE, BUTTON_STYLE_DEFAULT, BUTTON_STYLE_NONE, \ BUTTON_LOCATION_LEFT, BUTTON_LOCATION_RIGHT, BUTTON_LOCATION_TOP, \ BUTTON_LOCATION_BOTTOM, FORM_POSITION_TOP_LEFT -from analytical.tests.utils import TagTestCase, override_settings, \ - SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -41,7 +41,7 @@ class SnapEngageTestCase(TagTestCase): 'SnapABug.addButton("ec329c69-0bf0-4db8-9b77-3f8150fb977e","0",' '"55%");' in r, r) - @override_settings(SNAPENGAGE_WIDGET_ID=SETTING_DELETED) + @override_settings(SNAPENGAGE_WIDGET_ID=None) def test_no_site_id(self): self.assertRaises(AnalyticalException, SnapEngageNode) diff --git a/analytical/tests/test_tag_spring_metrics.py b/analytical/tests/test_tag_spring_metrics.py index a69cb5d..5256d55 100644 --- a/analytical/tests/test_tag_spring_metrics.py +++ b/analytical/tests/test_tag_spring_metrics.py @@ -7,10 +7,10 @@ import re from django.contrib.auth.models import User, AnonymousUser from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.spring_metrics import SpringMetricsNode -from analytical.tests.utils import TagTestCase, override_settings, \ - SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -28,7 +28,7 @@ class SpringMetricsTagTestCase(TagTestCase): r = SpringMetricsNode().render(Context({})) self.assertTrue("_springMetq.push(['id', '12345678']);" in r, r) - @override_settings(SPRING_METRICS_TRACKING_ID=SETTING_DELETED) + @override_settings(SPRING_METRICS_TRACKING_ID=None) def test_no_site_id(self): self.assertRaises(AnalyticalException, SpringMetricsNode) diff --git a/analytical/tests/test_tag_uservoice.py b/analytical/tests/test_tag_uservoice.py index bebce83..5ff6a8b 100644 --- a/analytical/tests/test_tag_uservoice.py +++ b/analytical/tests/test_tag_uservoice.py @@ -2,13 +2,11 @@ Tests for the UserVoice tags and filters. """ -from django.contrib.auth.models import User, AnonymousUser -from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.uservoice import UserVoiceNode -from analytical.tests.utils import TagTestCase, override_settings, \ - SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -32,7 +30,7 @@ class UserVoiceTagTestCase(TagTestCase): r = self.render_tag('uservoice', 'uservoice') self.assertIn("widget.uservoice.com/abcdefghijklmnopqrst.js", r) - @override_settings(USERVOICE_WIDGET_KEY=SETTING_DELETED) + @override_settings(USERVOICE_WIDGET_KEY=None) def test_no_key(self): self.assertRaises(AnalyticalException, UserVoiceNode) diff --git a/analytical/tests/test_tag_woopra.py b/analytical/tests/test_tag_woopra.py index 42dd8fa..8eb4ec2 100644 --- a/analytical/tests/test_tag_woopra.py +++ b/analytical/tests/test_tag_woopra.py @@ -5,10 +5,10 @@ Tests for the Woopra template tags and filters. from django.contrib.auth.models import User, AnonymousUser from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.templatetags.woopra import WoopraNode -from analytical.tests.utils import TagTestCase, override_settings, \ - SETTING_DELETED +from analytical.tests.utils import TagTestCase from analytical.utils import AnalyticalException @@ -26,7 +26,7 @@ class WoopraTagTestCase(TagTestCase): r = WoopraNode().render(Context({})) self.assertTrue('var woo_settings = {"domain": "example.com"};' in r, r) - @override_settings(WOOPRA_DOMAIN=SETTING_DELETED) + @override_settings(WOOPRA_DOMAIN=None) def test_no_domain(self): self.assertRaises(AnalyticalException, WoopraNode) diff --git a/analytical/tests/test_utils.py b/analytical/tests/test_utils.py index 071059e..53f59d5 100644 --- a/analytical/tests/test_utils.py +++ b/analytical/tests/test_utils.py @@ -3,41 +3,25 @@ Tests for the analytical.utils module. """ from django.conf import settings -from django.contrib.sites.models import Site from django.http import HttpRequest from django.template import Context +from django.test.utils import override_settings from analytical.utils import ( get_domain, is_internal_ip, get_required_setting, AnalyticalException) -from analytical.tests.utils import ( - TestCase, override_settings, with_apps, SETTING_DELETED) +from analytical.tests.utils import TestCase class SettingDeletedTestCase(TestCase): - @override_settings(USER_ID=SETTING_DELETED) - def test_deleted_setting_raises_exception(self): - self.assertRaises(AttributeError, getattr, settings, "USER_ID") - @override_settings(USER_ID=1) - def test_only_disable_within_context_manager(self): - """ - Make sure deleted settings returns once the block exits. - """ - self.assertEqual(settings.USER_ID, 1) - - with override_settings(USER_ID=SETTING_DELETED): - self.assertRaises(AttributeError, getattr, settings, "USER_ID") - - self.assertEqual(settings.USER_ID, 1) - - @override_settings(USER_ID=SETTING_DELETED) + @override_settings(USER_ID=None) def test_get_required_setting(self): """ Make sure using get_required_setting fails in the right place. """ # only available in python >= 2.7 if hasattr(self, 'assertRaisesRegexp'): - with self.assertRaisesRegexp(AnalyticalException, "^USER_ID setting: not found$"): + with self.assertRaisesRegexp(AnalyticalException, "^USER_ID setting is set to None$"): user_id = get_required_setting("USER_ID", "\d+", "invalid USER_ID") else: self.assertRaises(AnalyticalException, diff --git a/analytical/tests/utils.py b/analytical/tests/utils.py index 8c29fec..ee95c51 100644 --- a/analytical/tests/utils.py +++ b/analytical/tests/utils.py @@ -4,103 +4,8 @@ Testing utilities. from __future__ import with_statement -import copy - -from django.conf import settings, UserSettingsHolder -from django.core.management import call_command -from django.db.models import loading from django.template import Template, Context, RequestContext from django.test.testcases import TestCase -from django.utils.functional import wraps - - -SETTING_DELETED = object() - - -# Backported adapted from Django trunk (r16377) -class override_settings(object): - """ - Temporarily override Django settings. - - Can be used as either a decorator on test classes/functions or as - a context manager inside test functions. - - In either case it temporarily overrides django.conf.settings so - that you can test how code acts when certain settings are set to - certain values or deleted altogether with SETTING_DELETED. - - >>> @override_settings(FOOBAR=42) - >>> class TestBaz(TestCase): - >>> # settings.FOOBAR == 42 for all tests - >>> - >>> @override_settings(FOOBAR=43) - >>> def test_widget(self): - >>> # settings.FOOBAR == 43 for just this test - >>> - >>> with override_settings(FOOBAR=44): - >>> # settings.FOOBAR == 44 just inside this block - >>> pass - >>> - >>> # settings.FOOBAR == 43 inside the test - """ - def __init__(self, **kwargs): - self.options = kwargs - self.wrapped = settings._wrapped - - def __enter__(self): - self.enable() - - def __exit__(self, exc_type, exc_value, traceback): - self.disable() - - def __call__(self, test_func): - from django.test import TransactionTestCase - if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase): - # When decorating a class, we need to construct a new class - # with the same name so that the test discovery tools can - # get a useful name. - def _pre_setup(innerself): - self.enable() - test_func._pre_setup(innerself) - def _post_teardown(innerself): - test_func._post_teardown(innerself) - self.disable() - inner = type( - test_func.__name__, - (test_func,), - { - '_pre_setup': _pre_setup, - '_post_teardown': _post_teardown, - '__module__': test_func.__module__, - }) - else: - @wraps(test_func) - def inner(*args, **kwargs): - with self: - return test_func(*args, **kwargs) - return inner - - def enable(self): - class OverrideSettingsHolder(UserSettingsHolder): - def __getattr__(self, name): - if name == "default_settings": - return self.__dict__["default_settings"] - return getattr(self.default_settings, name) - - override = OverrideSettingsHolder(copy.copy(settings._wrapped)) - for key, new_value in self.options.items(): - if new_value is SETTING_DELETED: - try: - delattr(override.default_settings, key) - except AttributeError: - pass - else: - setattr(override, key, new_value) - settings._wrapped = override - - def disable(self): - settings._wrapped = self.wrapped - def run_tests(): """ @@ -109,30 +14,19 @@ def run_tests(): Sets the return code to the number of failed tests. """ import sys - from django.test.simple import DjangoTestSuiteRunner - runner = DjangoTestSuiteRunner() + import django + try: + django.setup() + except AttributeError: + pass + try: + from django.test.runner import DiscoverRunner as TestRunner + except ImportError: + from django.test.simple import DjangoTestSuiteRunner as TestRunner + runner = TestRunner() sys.exit(runner.run_tests(["analytical"])) -def with_apps(*apps): - """ - Class decorator that makes sure the passed apps are present in - INSTALLED_APPS. - """ - apps_set = set(settings.INSTALLED_APPS) - apps_set.update(apps) - return override_settings(INSTALLED_APPS=list(apps_set)) - - -def without_apps(*apps): - """ - Class decorator that makes sure the passed apps are not present in - INSTALLED_APPS. - """ - apps_list = [a for a in settings.INSTALLED_APPS if a not in apps] - return override_settings(INSTALLED_APPS=apps_list) - - class TagTestCase(TestCase): """ Tests for a template tag. diff --git a/analytical/utils.py b/analytical/utils.py index db31706..f8d4a32 100644 --- a/analytical/utils.py +++ b/analytical/utils.py @@ -22,6 +22,8 @@ def get_required_setting(setting, value_re, invalid_msg): value = getattr(settings, setting) except AttributeError: raise AnalyticalException("%s setting: not found" % setting) + if value is None: + raise AnalyticalException("%s setting is set to None" % setting) value = str(value) if not value_re.search(value): raise AnalyticalException("%s setting: %s: '%s'" diff --git a/setup.py b/setup.py index 472ac3a..e016ca5 100644 --- a/setup.py +++ b/setup.py @@ -69,6 +69,8 @@ setup( 'Programming Language :: Python', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Software Development :: Libraries :: Python Modules', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', ], platforms = ['any'], url = 'http://github.com/jcassee/django-analytical', diff --git a/tox.ini b/tox.ini index 6f705c5..60765da 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,8 @@ [tox] envlist = py2.6-django1.4,py2.6-django1.5,py2.6-django1.6, - py2.7-django1.4,py2.7-django1.5,py2.7-django1.6 + py2.7-django1.4,py2.7-django1.5,py2.7-django1.6, + py2.7-django1.7 [testenv] commands = python -Wall setup.py test @@ -45,3 +46,7 @@ deps = Django>=1.6,<1.7 [testenv:py2.7-django1.6] basepython = python2.7 deps = Django>=1.6,<1.7 + +[testenv:py2.7-django1.7] +basepython = python2.7 +deps = Django>=1.7,<1.8