pax_global_header00006660000000000000000000000064126424346030014516gustar00rootroot0000000000000052 comment=3c1fbbb363b7725ce8b1f8ecd92e460baa7724ff django-setuptest-0.2.1/000077500000000000000000000000001264243460300150165ustar00rootroot00000000000000django-setuptest-0.2.1/.gitignore000066400000000000000000000000301264243460300167770ustar00rootroot00000000000000*.pyc *.swp *.egg-info/ django-setuptest-0.2.1/AUTHORS.rst000066400000000000000000000002031264243460300166700ustar00rootroot00000000000000Authors ======= Praekelt Foundation ------------------- * Shaun Sephton * Hedley Roos Contributors ------------ * Jannis Leidel django-setuptest-0.2.1/CHANGELOG.rst000066400000000000000000000032731264243460300170440ustar00rootroot00000000000000Changelog ========= 0.2.1 (2016-01-04) ------------------ #. Support Django 1.9. 0.2 (2015-10-30) ---------------- #. Django 1.8 support (lamby). #. Exclude south_migrations from PEP8 checks (mikebryant). 0.1.6 (2015-01-13) ------------------ #. Added saving of raw coverage data 0.1.5 (2014-09-11) ------------------ #. Introduced support for Django 1.7. 0.1.4 (2013-06-21) ------------------ #. South patches the test management command to handle the SOUTH_TESTS_MIGRATE setting. Apply that patch if South is installed. 0.1.3 (2013-05-23) ------------------ #. Python 3 compatibility. 0.1.2 (2012-07-02) ------------------ #. Exclude South migrations from Pep8. 0.1.1 (2012-06-19) ------------------ #. Corrections to support PEP8 backwards incompatible API update. 0.0.9 (2012-06-15) ------------------ #. Now supports running specific test classes or methods via the label option. 0.0.8 (2012-06-13) ------------------ #. Added autoreload option restarting testrunner on code change detection. 0.0.7 (2012-06-04) ------------------ #. Refactor into a test command allowing for failfast commandline argument. 0.0.6 (2011-09-08) ------------------ #. Refactor, cleanup, self contained suite class. 0.0.5 (2011-09-06) ------------------ #. Added frame hack to resolve packages and py_modules to test, no longer needs app specific test suite. 0.0.4 (2011-09-06) ------------------ #. Refactored the app to use a callback style approach instead of monkey patching. Thanks `jezdez `_. 0.0.3 (2011-08-30) ------------------ #. More robust test settings import. 0.0.2 (2011-08-29) ------------------ #. Repeat Pep 8 errors. 0.0.1 (2011-08-29) ------------------ #. Initial release. django-setuptest-0.2.1/LICENSE000066400000000000000000000027351264243460300160320ustar00rootroot00000000000000Copyright (c) Praekelt Consulting All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Praekelt Consulting nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRAKELT CONSULTING BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-setuptest-0.2.1/MANIFEST.in000066400000000000000000000001151264243460300165510ustar00rootroot00000000000000include AUTHORS.rst include CHANGELOG.rst include LICENSE include README.rst django-setuptest-0.2.1/README.rst000066400000000000000000000123351264243460300165110ustar00rootroot00000000000000Django Setuptest ================ **Simple module enabling Django app testing via $ python setup.py test.** .. contents:: Contents :depth: 5 Normally when you execute ``$ python setup.py test`` for Django related modules you're almost certain to run into ``DJANGO_SETTINGS_MODULE`` environment variable issues, e.g.:: ImportError: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined. This module overcomes this by configuring the ``DJANGO_SETTINGS_MODULE`` environment variable before executing your test suite. As a bonus it also generates Coverage_ and `PEP 8`_ reports as part of the test. Installation ------------ #. Provide a ``test_suite`` argument to the setup call specifying the ``setuptest.setuptest.SetupTestSuite`` test suite, e.g.:: setup( # ... test_suite='setuptest.setuptest.SetupTestSuite', ) Alternatively provide a ``cmdclass`` ``test`` argument to the setup call specifying the ``setuptest.test`` command, e.g.:: from setuptest import test #... setup( # ... cmdclass={'test': test}, ) This overrides Python's builtin ``test`` command to enable the Django testrunner as well as allowing you to pass ``--failfast`` as a commandline argument, i.e.:: $ python setup.py test --failfast For the ``cmdclass`` method to work ``django-setuptools`` should be installed and available in your Python path prior to running the ``test`` command, in which case ``django-setuptest`` is not required to be specified as part of the ``tests_required`` argument as detailed next. #. Provide a ``tests_require`` argument to the setup call including ``django-setuptest`` (required only if not already installed) and other package dependencies needed to execute the tests, e.g.:: setup( # ... tests_require=( 'django-setuptest', ), ) #. Specify your test specific Django settings in a ``test_settings`` module in the same path as your app's ``setup.py``. These settings will be used when executing the tests, e.g. in ``test_settings.py``:: DATABASE_ENGINE = 'sqlite3' INSTALLED_APPS = ( 'myapp', ) #. In order for the test suite to find your tests you must provide either a ``packages`` or ``py_modules`` argument to the setup call, e.g.:: from setuptools import setup, find_packages setup( # ... packages=find_packages(), ) # Or alternatively... setup( # ... py_modules=['myapp'], ) Usage ----- Once correctly configured you can execute tests from the command line:: $ python setup.py test or, if you want the test suite to stop after the first test failure is detected:: $ python setup.py test --failfast This should output your test results as well as Coverage_ and `PEP 8`_ reports. .. note:: An XML Coverage report is generated in a file called ``coverage.xml`` and a PEP8 report is generated in a file called ``pep8.txt`` To mute the output of the Coverage_ and `PEP 8`_ reports provide the ``--quiet`` option:: $ python setup.py test --quiet To automatically restart the test runner when code changes are detected (similar to how ``runserver`` restarts) provide the ``--autoreload`` option:: $ python setup.py test --autoreload To only run tests for a particular test case specify the test case as the ``--label`` option:: $ python setup.py test --label app.TestCase Or for a particular test method specify the test case's test method as the ``--label`` option:: $ python setup.py test --label app.TestCase.test_method Sample Output ------------- Example output of dummy test including Coverage_ and `PEP 8`_ reports:: $ python setup.py test running test running egg_info writing django_dummy.egg-info/PKG-INFO writing top-level names to django_dummy.egg-info/top_level.txt writing dependency_links to django_dummy.egg-info/dependency_links.txt reading manifest file 'django_dummy.egg-info/SOURCES.txt' reading manifest template 'MANIFEST.in' writing manifest file 'django_dummy.egg-info/SOURCES.txt' running build_ext Creating test database for alias 'default'... E ====================================================================== ERROR: test_something (dummy.tests.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/user/tmp/django-dummy/dummy/tests/__init__.py", line 6, in test_something raise NotImplementedError('Test not implemented. Bad developer!') NotImplementedError: Test not implemented. Bad developer! ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (errors=1) Destroying test database for alias 'default'... Coverage Report: Name Stmts Miss Cover Missing ----------------------------------------------- dummy/models 20 2 90% 22, 55 PEP8 Report: dummy/tests/__init__.py:6:1: W391 blank line at end of file $ .. _Coverage: http://nedbatchelder.com/code/coverage/ .. _`PEP 8`: http://www.python.org/dev/peps/pep-0008/ django-setuptest-0.2.1/setup.py000066400000000000000000000021111264243460300165230ustar00rootroot00000000000000import codecs from os import path from setuptools import setup, find_packages def read(filepath): filepath = path.join(path.dirname(__file__), filepath) return codecs.open(filepath, 'r', 'utf-8').read() description = read('README.rst') + read('AUTHORS.rst') + read('CHANGELOG.rst') setup( name='django-setuptest', version='0.2.1', description='Simple test suite enabling Django app testing via $ python setup.py test', long_description=description, author='Praekelt Foundation', author_email='dev@praekelt.com', license='BSD', url='http://github.com/praekelt/django-setuptest', packages=find_packages(), install_requires=[ 'coverage', 'django', 'pep8', ], classifiers=[ "Programming Language :: Python", "License :: OSI Approved :: BSD License", "Development Status :: 4 - Beta", "Operating System :: OS Independent", "Framework :: Django", "Intended Audience :: Developers", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", ], zip_safe=False, ) django-setuptest-0.2.1/setuptest/000077500000000000000000000000001264243460300170565ustar00rootroot00000000000000django-setuptest-0.2.1/setuptest/__init__.py000066400000000000000000000030721264243460300211710ustar00rootroot00000000000000import os import sys from setuptools.command.test import test as TestCommand from .setuptest import LabelException class test(TestCommand): user_options = TestCommand.user_options + [ ('autoreload', 'a', "Test suite will restart when code changes are detected."), ('failfast', 'f', "Test suite will stop running after the first test failure is detected."), ('label=', 'l', "Only run tests for specified label. Label should be of the form app.TestClass or app.TestClass.test_method."), ] def initialize_options(self): TestCommand.initialize_options(self) self.test_suite = 'setuptest.setuptest.SetupTestSuite' self.autoreload = 0 self.failfast = 0 self.label = 0 def run_tests(self): auto_reload = False if '-a' in sys.argv or '--autoreload' in sys.argv: auto_reload = True if auto_reload: from django.utils.autoreload import restart_with_reloader, reloader_thread if os.environ.get("RUN_MAIN") == "true": try: TestCommand.run_tests(self) except LabelException: sys.exit(1) except: pass try: reloader_thread() except KeyboardInterrupt: pass else: try: sys.exit(restart_with_reloader()) except KeyboardInterrupt: pass else: return TestCommand.run_tests(self) django-setuptest-0.2.1/setuptest/setuptest.py000066400000000000000000000217251264243460300214770ustar00rootroot00000000000000import argparse import django import pep8 import sys import time import traceback import unittest from coverage import coverage, misc from distutils import log from django.utils.six import StringIO class LabelException(Exception): pass class SetupTestSuite(unittest.TestSuite): """ Test Suite configuring Django settings and using DiscoverRunner or DjangoTestSuiteRunner as the test runner. Also runs PEP8 and Coverage checks. """ def __init__(self, *args, **kwargs): self.cov = coverage() self.cov.start() self.configure() self.packages = self.resolve_packages() parser = argparse.ArgumentParser() parser.add_argument('-a', '--autoreload', dest='autoreload', action='store_const', const=True, default=False,) parser.add_argument('-f', '--failfast', dest='failfast', action='store_const', const=True, default=False,) parser.add_argument('-l', '--label', dest='label') self.options = vars(parser.parse_args(sys.argv[2:])) sys.argv = sys.argv[:2] runner_options = { 'verbosity': 1, 'interactive': True, 'failfast': False, } if django.VERSION >= (1, 8): from django.test.runner import DiscoverRunner self.test_runner = DiscoverRunner(**runner_options) tests = self.test_runner.build_suite() else: from django.test.simple import DjangoTestSuiteRunner self.test_runner = DjangoTestSuiteRunner(**runner_options) tests = self.build_tests() super(SetupTestSuite, self).__init__(tests=tests, *args, **kwargs) # South patches the test management command to handle the # SOUTH_TESTS_MIGRATE setting. Apply that patch if South is installed. if django.VERSION < (1,7): try: from south.management.commands import patch_for_test_db_setup patch_for_test_db_setup() except ImportError: pass self.test_runner.setup_test_environment() self.old_config = self.test_runner.setup_databases() def handle_label_exception(self, exception): """ Check whether or not the exception was caused due to a bad label being provided. If so raise LabelException which will cause an exit, otherwise continue. The check looks for particular error messages, which obviously sucks. TODO: Implement a cleaner test. """ markers = [ 'no such test method', 'should be of the form app.TestCase or app.TestCase.test_method', 'App with label', 'does not refer to a test', ] if any(marker in exception.message for marker in markers): log.info(exception) raise LabelException(exception) else: raise exception def build_tests(self): """ Build tests for inclusion in suite from resolved packages for <= 1.8 TODO: Cleanup/simplify this method, flow too complex, too much duplication. """ from django.core.exceptions import ImproperlyConfigured from django.test.simple import build_suite, build_test try: from django.apps import apps get_app = apps.get_app_config except ImportError: from django.db.models import get_app tests = [] packages = [self.options['label'], ] if \ self.options['label'] else self.packages for package in packages: try: if not self.options['autoreload']: if self.options['label']: try: tests.append(build_test(package)) except (ImproperlyConfigured, ValueError) as e: self.handle_label_exception(e) else: app = get_app(package) tests.append(build_suite(app)) else: # Wait for exceptions to be resolved. exception = None while True: try: if self.options['label']: try: tests.append(build_test(package)) except (ImproperlyConfigured, ValueError) as e: self.handle_label_exception(e) else: app = get_app(package) tests.append(build_suite(app)) break except LabelException: raise except Exception as e: if exception != str(e): traceback.print_exc() exception = str(e) time.sleep(1) except ImproperlyConfigured as e: log.info("Warning: %s" % traceback.format_exc()) except ImportError as e: log.info("Warning: %s" % traceback.format_exc()) return tests def configure(self): """ Configures Django settings. """ import django from django.conf import settings try: from django.utils.importlib import import_module except ImportError: from importlib import import_module try: test_settings = import_module('test_settings') except ImportError as e: log.info('ImportError: Unable to import test settings: %s' % e) sys.exit(1) setting_attrs = {} for attr in dir(test_settings): if '__' not in attr: setting_attrs[attr] = getattr(test_settings, attr) if not settings.configured: settings.configure(**setting_attrs) if hasattr(django, 'setup'): django.setup() def coverage_report(self): """ Outputs Coverage report to screen and coverage.xml. """ verbose = '--quiet' not in sys.argv self.cov.stop() if verbose: log.info("\nCoverage Report:") try: include = ['%s*' % package for package in self.packages] omit = ['*tests*'] self.cov.report(include=include, omit=omit) self.cov.save() self.cov.xml_report(include=include, omit=omit) except misc.CoverageException as e: log.info("Coverage Exception: %s" % e) def resolve_packages(self): """ Frame hack to determine packages contained in module for testing. We ignore submodules (those containing '.') """ f = sys._getframe() while f: if 'self' in f.f_locals: locals_self = f.f_locals['self'] py_modules = getattr(locals_self, 'py_modules', None) packages = getattr(locals_self, 'packages', None) top_packages = [] if py_modules or packages: if py_modules: for module in py_modules: if '.' not in module: top_packages.append(module) if packages: for package in packages: if '.' not in package: top_packages.append(package) return list(set(top_packages)) f = f.f_back def pep8_report(self): """ Outputs PEP8 report to screen and pep8.txt. """ verbose = '--quiet' not in sys.argv if verbose: # Hook into stdout. old_stdout = sys.stdout sys.stdout = mystdout = StringIO() # Run Pep8 checks, excluding South migrations. pep8_style = pep8.StyleGuide() pep8_style.options.exclude.append('migrations') pep8_style.options.exclude.append('south_migrations') pep8_style.check_files(self.packages) # Restore stdout. sys.stdout = old_stdout # Save result to pep8.txt. result = mystdout.getvalue() output = open('pep8.txt', 'w') output.write(result) output.close() # Return Pep8 result if result: log.info("\nPEP8 Report:") log.info(result) def run(self, result, *args, **kwargs): """ Run the test, teardown the environment and generate reports. """ result.failfast = self.options['failfast'] result = super(SetupTestSuite, self).run(result, *args, **kwargs) self.test_runner.teardown_databases(self.old_config) self.test_runner.teardown_test_environment() self.coverage_report() self.pep8_report() return result