pax_global_header00006660000000000000000000000064133552311370014515gustar00rootroot0000000000000052 comment=42eae06b60f7f91e91473e49e6f295221339c3ba django-nose-1.4.6/000077500000000000000000000000001335523113700137315ustar00rootroot00000000000000django-nose-1.4.6/.gitignore000066400000000000000000000004641335523113700157250ustar00rootroot00000000000000# Python binaries *.py[cod] # Editor backups, OS files *.swp *~ .DS_Store # Distutils cruft *.egg-info South-*.egg/ build/ dist/ # Unit test / coverage reports .tox/ .coverage .coverage.* .noseids nosetests.xml htmlcov/ testapp.sqlite3 # Sphinx documentation build docs/_build/* # pyenv .python-version django-nose-1.4.6/.travis.yml000066400000000000000000000160221335523113700160430ustar00rootroot00000000000000sudo: false language: python env: global: - COVERAGE=1 RUNTEST_ARGS="-v --noinput" matrix: include: # Quality checks - env: TOXENV=flake8 python: "3.5" - env: TOXENV=docs python: "3.5" # Supported Python / Django versions w/ SQLite - env: TOXENV=py35-django-21 python: "3.5" - env: TOXENV=py36-django-21 python: "3.6" - env: TOXENV=py37-django-21 python: "3.7" sudo: required dist: xenial - env: TOXENV=py35-django-20 python: "3.5" - env: TOXENV=py36-django-20 python: "3.6" - env: TOXENV=py27-django-111 python: "2.7" - env: TOXENV=py36-django-111 python: "3.6" - env: TOXENV=py27-django-110 python: "2.7" - env: TOXENV=py35-django-110 python: "3.5" - env: TOXENV=py27-django-19 python: "2.7" - env: TOXENV=py35-django-19 python: "3.5" - env: TOXENV=py27-django-18 python: "2.7" - env: TOXENV=py34-django-18 python: "3.4" # Test with PostgreSQL - env: TOXENV=py35-django-21-postgres DATABASE_URL="postgres://postgres@localhost:5432/py35-django-21-postgres" python: "3.5" services: postgresql - env: TOXENV=py36-django-21-postgres DATABASE_URL="postgres://postgres@localhost:5432/py36-django-21-postgres" python: "3.6" services: postgresql - env: TOXENV=py37-django-21-postgres DATABASE_URL="postgres://postgres@localhost:5432/py37-django-21-postgres" python: "3.7" sudo: required dist: xenial services: postgresql - env: TOXENV=py35-django-20-postgres DATABASE_URL="postgres://postgres@localhost:5432/py35-django-20-postgres" python: "3.5" services: postgresql - env: TOXENV=py36-django-20-postgres DATABASE_URL="postgres://postgres@localhost:5432/py36-django-20-postgres" python: "3.6" services: postgresql - env: TOXENV=py37-django-20-postgres DATABASE_URL="postgres://postgres@localhost:5432/py37-django-20-postgres" python: "3.7" sudo: required dist: xenial services: postgresql - env: TOXENV=py27-django-111-postgres DATABASE_URL="postgres://postgres@localhost:5432/py27-django-111-postgres" python: "2.7" services: postgresql - env: TOXENV=py36-django-111-postgres DATABASE_URL="postgres://postgres@localhost:5432/py36-django-111-postgres" python: "3.6" services: postgresql - env: TOXENV=py27-django-110-postgres DATABASE_URL="postgres://postgres@localhost:5432/py27-django-110-postgres" python: "2.7" services: postgresql - env: TOXENV=py35-django-110-postgres DATABASE_URL="postgres://postgres@localhost:5432/py35-django-110-postgres" python: "3.5" services: postgresql - env: TOXENV=py27-django-19-postgres DATABASE_URL="postgres://postgres@localhost:5432/py27-django-19-postgres" python: "2.7" services: postgresql - env: TOXENV=py35-django-19-postgres DATABASE_URL="postgres://postgres@localhost:5432/py35-django-19-postgres" python: "3.5" services: postgresql - env: TOXENV=py27-django-18-postgres DATABASE_URL="postgres://postgres@localhost:5432/py27-django-18-postgres" python: "2.7" services: postgresql # Test with MySQL - env: TOXENV=py35-django-21-mysql DATABASE_URL="mysql://travis@localhost:3306/py35-django-21-mysql" python: "3.5" services: mysql - env: TOXENV=py36-django-21-mysql DATABASE_URL="mysql://travis@localhost:3306/py36-django-21-mysql" python: "3.6" services: mysql - env: TOXENV=py37-django-21-mysql DATABASE_URL="mysql://travis@localhost:3306/py37-django-21-mysql" python: "3.7" sudo: required dist: xenial services: mysql - env: TOXENV=py35-django-20-mysql DATABASE_URL="mysql://travis@localhost:3306/py35-django-20-mysql" python: "3.5" services: mysql - env: TOXENV=py36-django-20-mysql DATABASE_URL="mysql://travis@localhost:3306/py36-django-20-mysql" python: "3.6" services: mysql - env: TOXENV=py37-django-20-mysql DATABASE_URL="mysql://travis@localhost:3306/py37-django-20-mysql" python: "3.7" sudo: required dist: xenial services: mysql - env: TOXENV=py27-django-111-mysql DATABASE_URL="mysql://travis@localhost:3306/py27-django-111-mysql" python: "2.7" services: mysql - env: TOXENV=py36-django-111-mysql DATABASE_URL="mysql://travis@localhost:3306/py36-django-111-mysql" python: "3.6" services: mysql - env: TOXENV=py27-django-110-mysql DATABASE_URL="mysql://travis@localhost:3306/py27-django-110-mysql" python: "2.7" services: mysql - env: TOXENV=py35-django-110-mysql DATABASE_URL="mysql://travis@localhost:3306/py35-django-110-mysql" python: "3.5" services: mysql - env: TOXENV=py27-django-19-mysql DATABASE_URL="mysql://travis@localhost:3306/py27-django-19-mysql" python: "2.7" services: mysql - env: TOXENV=py35-django-19-mysql DATABASE_URL="mysql://travis@localhost:3306/py35-django-19-mysql" python: "3.5" services: mysql - env: TOXENV=py27-django-18-mysql DATABASE_URL="mysql://travis@localhost:3306/py27-django-18-mysql" python: "2.7" services: mysql # Django master is allowed to fail - env: TOXENV=py35-django-master python: "3.5" - env: TOXENV=py36-django-master python: "3.6" - env: TOXENV=py37-django-master python: "3.7" sudo: required dist: xenial - env: TOXENV=py35-django-master-mysql DATABASE_URL="mysql://travis@localhost:3306/py35-django-master-mysql" python: "3.5" language: pyth services: mysql - env: TOXENV=py36-django-master-postgres DATABASE_URL="postgres://postgres@localhost:5432/py36-django-master-postgres" python: "3.6" services: postgresql allow_failures: # Master is allowed to fail - env: TOXENV=py35-django-master - env: TOXENV=py36-django-master - env: TOXENV=py37-django-master - env: TOXENV=py35-django-master-mysql DATABASE_URL="mysql://travis@localhost:3306/py35-django-master-mysql" - env: TOXENV=py36-django-master-postgres DATABASE_URL="postgres://postgres@localhost:5432/py36-django-master-postgres" install: - pip install tox coveralls before_script: - coverage erase - bash -c "if [[ \"$DATABASE_URL\" == postgres* ]]; then psql -c 'create database \"$TOXENV\";' -U postgres; fi" - bash -c "if [[ \"$DATABASE_URL\" == mysql* ]]; then mysql -e 'create database IF NOT EXISTS \`$TOXENV\`;'; fi" script: tox after_success: coveralls django-nose-1.4.6/AUTHORS.rst000066400000000000000000000060321335523113700156110ustar00rootroot00000000000000======= Credits ======= **django-nose** was created by Jeff Balogh in 2009, which is a really long time ago in the Django world. It keeps running because of the contributions of volunteers. Thank you to everyone who uses django-nose, keeps your projects up-to-date, files detailed bugs, and submits patches. Maintainers ----------- * Jeff Balogh (`jbalogh `_) * Erik Rose (`erikrose `_) * James Socol (`jscol `_) * Rob Hudson (`robhudson `_) * John Whitlock (`jwhitlock `_) Contributors ------------ These non-maintainers have contributed code to a django-nose release: * Adam DePue (`adepue `_) * Alexey Evseev (`st4lk `_) * Alex (`alexjg `_) * Antti Kaihola (`akaihola `_) * Ash Christopher (`ashchristopher `_) * Blake Winton (`bwinton `_) * Brad Pitcher (`brad `_) * Camilo Nova (`camilonova `_) * Carl Meyer (`carljm `_) * Conrado Buhrer (`conrado `_) * David Baumgold (`singingwolfboy `_) * David Cramer (`dcramer `_) * Dmitry Gladkov (`dgladkov `_) * Ederson Mota (`edrmp `_) * Eric Zarowny (`ezarowny `_) * Eron Villarreal (`eroninjapan `_) * Fred Wenzel (`fwenzel `_) * Fábio Santos (`fabiosantoscode `_) * Ionel Cristian Mărieș (`ionelmc `_) * Jeremy Satterfield (`jsatt `_) * Johan Euphrosine (`proppy `_) * Kyle Robertson (`dvelyk `_) * Marius Gedminas (`mgedmin `_) * Martin Chase (`outofculture `_) * Matthias Bauer (`moeffju `_) * Michael Elsdörfer (`miracle2k `_) * Michael Kelly (`Osmose `_) * Peter Baumgartner (`ipmb `_) * Radek Simko (`radeksimko `_) * Ramiro Morales (`ramiro `_) * Rob Madole (`robmadole `_) * Roger Hu (`rogerhu `_) * Ross Lawley (`rozza `_) * Scott Sexton (`scottsexton `_) * Stephen Burrows (`melinath `_) * Sverre Johansen (`sverrejoh `_) * Tim Child (`timc3 `_) * Walter Doekes (`wdoekes `_) * Will Kahn-Greene (`willkg `_) * Yegor Roganov (`roganov `_) django-nose-1.4.6/CONTRIBUTING.rst000066400000000000000000000075311335523113700164000ustar00rootroot00000000000000============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. You can contribute in many ways: Types of Contributions ---------------------- Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/django-nose/django-nose/issues. If you are reporting a bug, please include: * The version of django, nose, and django-nose you are using, and any other applicable packages (``pip freeze`` will show current versions) * Any details about your local setup that might be helpful in troubleshooting. * Detailed steps to reproduce the bug. When someone submits a pull request to fix your bug, please try it out and report if it worked for you. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything untagged or tagged with "bug" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything untagged ot tagged with "feature" is open to whoever wants to implement it. django-nose is built on nose, which supports plugins. Consider implementing your feature as a plugin, maintained by the community using that feature, rather than adding to the django-nose codebase. Write Documentation ~~~~~~~~~~~~~~~~~~~ django-nose could always use more documentation, whether as part of the official django-nose, as code comments, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/django-nose/django-nose/issues. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) Get Started! ------------ Ready to contribute? Here's how to set up django-nose for local development. 1. Fork the `django-nose` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/django-nose.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: $ mkvirtualenv django-nose $ cd django-nose/ $ pip install -r requirements.txt $ ./manage.py migrate 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. Make sure existing tests continue to pass with your new code:: $ make qa 6. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: $ make qa-all 6. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 7. Submit a pull request through the GitHub website. Pull Request Guidelines ----------------------- Before you submit a pull request, check that it meets these guidelines: 1. The pull request should be in a branch. 2. The pull request should include tests. 3. You agree to license your contribution under the BSD license. 4. If the pull request adds functionality, the docs should be updated. 5. Make liberal use of `git rebase` to ensure clean commits on top of master. 6. The pull request should pass QA tests and work for supported Python / Django combinations. Check https://travis-ci.org/django-nose/django-nose/pull_requests and make sure that the tests pass for all supported Python versions. Tips ---- The django-nose testapp uses django-nose, so all of the features are available. To run a subset of tests:: $ ./manage.py test testapp/tests.py To mark failed tests:: $ ./manage.py test --failed To re-run only the failed tests:: $ ./manage.py test --failed django-nose-1.4.6/LICENSE000066400000000000000000000027601335523113700147430ustar00rootroot00000000000000Copyright (c) 2010, Jeff Balogh. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of django-nose 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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-nose-1.4.6/MANIFEST.in000066400000000000000000000007641335523113700154760ustar00rootroot00000000000000include AUTHORS.rst include CONTRIBUTING.rst include LICENSE include Makefile include README.rst include changelog.rst include contribute.json include manage.py include requirements.txt include requirements-rtd.txt include runtests.sh recursive-include docs Makefile conf.py *.rst make.bat .keep recursive-include django_nose *.py recursive-include testapp *.py recursive-include testapp/fixtures *.json recursive-include unittests *.py recursive-exclude * *.py[co] recursive-exclude * __pycache__ django-nose-1.4.6/Makefile000066400000000000000000000036401335523113700153740ustar00rootroot00000000000000.PHONY: clean clean-build clean-pyc clean-test docs qa lint coverage jslint qa-all install-jslint test test-all coverage-console release sdist help: @echo "clean - remove all artifacts" @echo "clean-build - remove build artifacts" @echo "clean-pyc - remove Python file artifacts" @echo "clean-test - remove test and coverage artifacts" @echo "coverage - check code coverage quickly with the default Python" @echo "docs - generate Sphinx HTML documentation" @echo "lint - check style with flake8" @echo "qa - run linters and test coverage" @echo "qa-all - run QA plus tox and packaging" @echo "release - package and upload a release" @echo "sdist - package" @echo "test - run tests quickly with the default Python" @echo "test-all - run tests on every Python version with tox" @echo "test-release - upload a release to the PyPI test server" clean: clean-build clean-pyc clean-test qa: lint coverage qa-all: qa sdist test-all clean-build: rm -fr build/ rm -fr dist/ rm -fr *.egg-info clean-pyc: find . \( -name \*.pyc -o -name \*.pyo -o -name __pycache__ \) -delete find . -name '*~' -delete clean-test: rm -fr .tox/ rm -f .coverage rm -fr htmlcov/ docs: $(MAKE) -C docs clean $(MAKE) -C docs html open docs/_build/html/index.html lint: flake8 . test: ./manage.py test test-all: COVERAGE=1 tox --skip-missing-interpreters coverage-console: coverage erase COVERAGE=1 ./runtests.sh coverage combine coverage report -m coverage: coverage-console coverage html open htmlcov/index.html release: sdist twine upload dist/* python -m webbrowser -n https://pypi.python.org/pypi/django-nose # Add [test] section to ~/.pypirc, https://test.pypi.org/legacy/ test-release: sdist twine upload --repository test dist/* python -m webbrowser -n https://testpypi.python.org/pypi/django-nose sdist: clean python setup.py sdist bdist_wheel ls -l dist check-manifest pyroma dist/`ls -t dist | grep tar.gz | head -n1` django-nose-1.4.6/README.rst000066400000000000000000000056521335523113700154300ustar00rootroot00000000000000=========== django-nose =========== .. image:: https://img.shields.io/pypi/v/django-nose.svg :alt: The PyPI package :target: https://pypi.python.org/pypi/django-nose .. image:: https://img.shields.io/travis/django-nose/django-nose/master.svg :alt: TravisCI Build Status :target: https://travis-ci.org/django-nose/django-nose .. image:: https://img.shields.io/coveralls/django-nose/django-nose/master.svg :alt: Coveralls Test Coverage :target: https://coveralls.io/r/django-nose/django-nose?branch=master .. Omit badges from docs **django-nose** provides all the goodness of `nose`_ in your Django tests, like: * Testing just your apps by default, not all the standard ones that happen to be in ``INSTALLED_APPS`` * Running the tests in one or more specific modules (or apps, or classes, or folders, or just running a specific test) * Obviating the need to import all your tests into ``tests/__init__.py``. This not only saves busy-work but also eliminates the possibility of accidentally shadowing test classes. * Taking advantage of all the useful `nose plugins`_ .. _nose: https://nose.readthedocs.io/en/latest/ .. _nose plugins: http://nose-plugins.jottit.com/ It also provides: * Fixture bundling, an optional feature which speeds up your fixture-based tests by a factor of 4 * Reuse of previously created test DBs, cutting 10 seconds off startup time * Hygienic TransactionTestCases, which can save you a DB flush per test * Support for various databases. Tested with MySQL, PostgreSQL, and SQLite. Others should work as well. django-nose requires nose 1.2.1 or later, and the `latest release`_ is recommended. It follows the `Django's support policy`_, supporting: * Django 1.8 (LTS) with Python 2.7, 3.4, or 3.5 * Django 1.9 with Python 2.7, 3.4, or 3.5 * Django 1.10 with Python 2.7, 3.4, or 3.5 * Django 1.11 (LTS) with Python 2.7, 3.4, 3.5, or 3.6 * Django 2.0 with Python 3.4, 3.5, 3.6, or 3.7 * Django 2.1 with Python 3.5, 3.6, or 3.7 .. _latest release: https://pypi.python.org/pypi/nose .. _Django's support policy: https://docs.djangoproject.com/en/1.8/internals/release-process/#supported-versions Installation ------------ You can get django-nose from PyPI with... : .. code-block:: shell $ pip install django-nose The development version can be installed with... : .. code-block:: shell $ pip install -e git://github.com/django-nose/django-nose.git#egg=django-nose Since django-nose extends Django's built-in test command, you should add it to your ``INSTALLED_APPS`` in ``settings.py``: .. code-block:: python INSTALLED_APPS = ( ... 'django_nose', ... ) Then set ``TEST_RUNNER`` in ``settings.py``: .. code-block:: python TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' Development ----------- :Code: https://github.com/django-nose/django-nose :Issues: https://github.com/django-nose/django-nose/issues?state=open :Docs: https://django-nose.readthedocs.io django-nose-1.4.6/changelog.rst000066400000000000000000000105111335523113700164100ustar00rootroot00000000000000Changelog --------- 1.4.6 (2018-10-03) ~~~~~~~~~~~~~~~~~~ * Document Django 2.0 and 2.1 support, no changes needed * Document Python 3.7 support 1.4.5 (2017-08-22) ~~~~~~~~~~~~~~~~~~ * Add Django 1.11 support 1.4.4 (2016-06-27) ~~~~~~~~~~~~~~~~~~ * Add Django 1.10 support * Drop Django 1.4 - 1.7, and Python 2.6 support * Drop South support 1.4.3 (2015-12-28) ~~~~~~~~~~~~~~~~~~ * Add Django 1.9 support * Support long options without equals signs, such as "--attr selected" * Support nose plugins using callback options * Support nose options without default values (jsatt) * Remove Django from install dependencies, to avoid accidental upgrades (jsocol, willkg) * Setting REUSE_DB to an empty value now disables REUSE_DB, instead of enabling it (wdoekes) 1.4.2 (2015-10-07) ~~~~~~~~~~~~~~~~~~ * Warn against using REUSE_DB=1 and FastFixtureTestCase in docs * REUSE_DB=1 uses new transaction management in Django 1.7, 1.8 (scottsexton) * Try to avoid accidentally using production database with REUSE_DB=1 (alexjg, eroninjapan) * Supported Django versions limited to current supported Django version 1.4, 1.7, and 1.8, as well as relevant Python versions. 1.4.1 (2015-06-29) ~~~~~~~~~~~~~~~~~~ * Fix version number (ezarowny) * Fix choice options, unbreaking nose-cover (aamirtharaj-rpx, jwhitlock) * Support 1.8 app loading system (dgladkov) * Support non-ASCII file names (singingwolfboy) * Better PEP8'd assertion names (roganov) 1.4 (2015-04-23) ~~~~~~~~~~~~~~~~ * Django 1.8 support (timc3, adepue, jwhitlock) * Support --testrunner option (st4lk) * Fix REUSE_DB second run in py3k (edrmp) 1.3 (2014-12-05) ~~~~~~~~~~~~~~~~ * Django 1.6 and 1.7 support (conrado, co3k, Nepherhotep, mbertheau) * Python 3.3 and 3.4 testing and support (frewsxcv, jsocol) 1.2 (2013-07-23) ~~~~~~~~~~~~~~~~ * Python 3 support (melinath and jonashaag) * Django 1.5 compat (fabiosantoscode) 1.1 (2012-05-19) ~~~~~~~~~~~~~~~~ * Django TransactionTestCases don't clean up after themselves; they leave junk in the DB and clean it up only on ``_pre_setup``. Thus, Django makes sure these tests run last. Now django-nose does, too. This means one fewer source of failures on existing projects. (Erik Rose) * Add support for hygienic TransactionTestCases. (Erik Rose) * Support models that are used only for tests. Just put them in any file imported in the course of loading tests. No more crazy hacks necessary. (Erik Rose) * Make the fixture bundler more conservative, fixing some conceivable situations in which fixtures would not appear as intended if a TransactionTestCase found its way into the middle of a bundle. (Erik Rose) * Fix an error that would surface when using SQLAlchemy with connection pooling. (Roger Hu) * Gracefully ignore the new ``--liveserver`` option introduced in Django 1.4; don't let it through to nose. (Adam DePue) 1.0 (2012-03-12) ~~~~~~~~~~~~~~~~ * New fixture-bundling plugin for avoiding needless fixture setup (Erik Rose) * Moved FastFixtureTestCase in from test-utils, so now all the fixture-bundling stuff is in one library. (Erik Rose) * Added the REUSE_DB setting for faster startup and shutdown. (Erik Rose) * Fixed a crash when printing options with certain verbosities. (Daniel Abel) * Broke hard dependency on MySQL. Support PostgreSQL. (Roger Hu) * Support SQLite, both memory- and disk-based. (Roger Hu and Erik Rose) * Nail down versions of the package requirements. (Daniel Mizyrycki) .. Omit older changes from package 0.1.3 (2010-04-15) ~~~~~~~~~~~~~~~~~~ * Even better coverage support (rozza) * README fixes (carljm and ionelmc) * optparse OptionGroups are handled better (outofculture) * nose plugins are loaded before listing options 0.1.2 (2010-08-14) ~~~~~~~~~~~~~~~~~~ * run_tests API support (carjm) * better coverage numbers (rozza & miracle2k) * support for adding custom nose plugins (kumar303) 0.1.1 (2010-06-01) ~~~~~~~~~~~~~~~~~~ * Cleaner installation (Michael Fladischer) 0.1 (2010-05-18) ~~~~~~~~~~~~~~~~ * Class-based test runner (Antti Kaihola) * Django 1.2 compatibility (Antti Kaihola) * Mapping Django verbosity to nose verbosity 0.0.3 (2009-12-31) ~~~~~~~~~~~~~~~~~~ * Python 2.4 support (Blake Winton) * GeoDjango spatial database support (Peter Baumgartner) * Return the number of failing tests on the command line 0.0.2 (2009-10-01) ~~~~~~~~~~~~~~~~~~ * rst readme (Rob Madole) 0.0.1 (2009-10-01) ~~~~~~~~~~~~~~~~~~ * birth! django-nose-1.4.6/contribute.json000066400000000000000000000012401335523113700167770ustar00rootroot00000000000000{ "name": "django-nose", "description": "Django test runner using nose.", "repository": { "url": "https://github.com/django-nose/django-nose", "license": "BSD", "tests": "https://travis-ci.org/django-nose/django-nose" }, "participate": { "home": "https://github.com/django-nose/django-nose", "docs": "https://github.com/django-nose/django-nose" }, "bugs": { "list": "https://github.com/django-nose/django-nose/issues", "report": "https://github.com/django-nose/django-nose/issues/new" }, "keywords": [ "django", "python", "nose", "testing" ] } django-nose-1.4.6/django_nose/000077500000000000000000000000001335523113700162175ustar00rootroot00000000000000django-nose-1.4.6/django_nose/__init__.py000066400000000000000000000005351335523113700203330ustar00rootroot00000000000000# coding: utf-8 """The django_nose module.""" from __future__ import unicode_literals from django_nose.runner import BasicNoseRunner, NoseTestSuiteRunner from django_nose.testcases import FastFixtureTestCase assert BasicNoseRunner assert NoseTestSuiteRunner assert FastFixtureTestCase VERSION = (1, 4, 6) __version__ = '.'.join(map(str, VERSION)) django-nose-1.4.6/django_nose/fixture_tables.py000066400000000000000000000151271335523113700216170ustar00rootroot00000000000000"""Unload fixtures by truncating tables rather than rebuilding. A copy of Django 1.3.0's stock loaddata.py, adapted so that, instead of loading any data, it returns the tables referenced by a set of fixtures so we can truncate them (and no others) quickly after we're finished with them. """ import os import gzip import zipfile from itertools import product from django.conf import settings from django.core import serializers from django.db import router, DEFAULT_DB_ALIAS try: from django.db.models import get_apps except ImportError: from django.apps import apps def get_apps(): """Emulate get_apps in Django 1.9 and later.""" return [a.models_module for a in apps.get_app_configs()] try: import bz2 has_bz2 = True except ImportError: has_bz2 = False def tables_used_by_fixtures(fixture_labels, using=DEFAULT_DB_ALIAS): """Get tables used by a fixture. Acts like Django's stock loaddata command, but, instead of loading data, return an iterable of the names of the tables into which data would be loaded. """ # Keep a count of the installed objects and fixtures fixture_count = 0 loaded_object_count = 0 fixture_object_count = 0 tables = set() class SingleZipReader(zipfile.ZipFile): def __init__(self, *args, **kwargs): zipfile.ZipFile.__init__(self, *args, **kwargs) if settings.DEBUG: assert len(self.namelist()) == 1, \ "Zip-compressed fixtures must contain only one file." def read(self): return zipfile.ZipFile.read(self, self.namelist()[0]) compression_types = { None: open, 'gz': gzip.GzipFile, 'zip': SingleZipReader } if has_bz2: compression_types['bz2'] = bz2.BZ2File app_module_paths = [] for app in get_apps(): if hasattr(app, '__path__'): # It's a 'models/' subpackage for path in app.__path__: app_module_paths.append(path) else: # It's a models.py module app_module_paths.append(app.__file__) app_fixtures = [ os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths] for fixture_label in fixture_labels: parts = fixture_label.split('.') if len(parts) > 1 and parts[-1] in compression_types: compression_formats = [parts[-1]] parts = parts[:-1] else: compression_formats = list(compression_types.keys()) if len(parts) == 1: fixture_name = parts[0] formats = serializers.get_public_serializer_formats() else: fixture_name, format = '.'.join(parts[:-1]), parts[-1] if format in serializers.get_public_serializer_formats(): formats = [format] else: formats = [] if not formats: # stderr.write(style.ERROR("Problem installing fixture '%s': %s is # not a known serialization format.\n" % (fixture_name, format))) return set() if os.path.isabs(fixture_name): fixture_dirs = [fixture_name] else: fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + [''] for fixture_dir in fixture_dirs: # stdout.write("Checking %s for fixtures...\n" % # humanize(fixture_dir)) label_found = False for combo in product([using, None], formats, compression_formats): database, format, compression_format = combo file_name = '.'.join( p for p in [ fixture_name, database, format, compression_format ] if p ) # stdout.write("Trying %s for %s fixture '%s'...\n" % \ # (humanize(fixture_dir), file_name, fixture_name)) full_path = os.path.join(fixture_dir, file_name) open_method = compression_types[compression_format] try: fixture = open_method(full_path, 'r') if label_found: fixture.close() # stderr.write(style.ERROR("Multiple fixtures named # '%s' in %s. Aborting.\n" % (fixture_name, # humanize(fixture_dir)))) return set() else: fixture_count += 1 objects_in_fixture = 0 loaded_objects_in_fixture = 0 # stdout.write("Installing %s fixture '%s' from %s.\n" # % (format, fixture_name, humanize(fixture_dir))) try: objects = serializers.deserialize( format, fixture, using=using) for obj in objects: objects_in_fixture += 1 cls = obj.object.__class__ if router.allow_syncdb(using, cls): loaded_objects_in_fixture += 1 tables.add(cls._meta.db_table) loaded_object_count += loaded_objects_in_fixture fixture_object_count += objects_in_fixture label_found = True except (SystemExit, KeyboardInterrupt): raise except Exception: fixture.close() # stderr.write( style.ERROR("Problem installing # fixture '%s': %s\n" % (full_path, ''.join(tra # ceback.format_exception(sys.exc_type, # sys.exc_value, sys.exc_traceback))))) return set() fixture.close() # If the fixture we loaded contains 0 objects, assume # that an error was encountered during fixture loading. if objects_in_fixture == 0: # stderr.write( style.ERROR("No fixture data found # for '%s'. (File format may be invalid.)\n" % # (fixture_name))) return set() except Exception: # stdout.write("No %s fixture '%s' in %s.\n" % \ (format, # fixture_name, humanize(fixture_dir))) pass return tables django-nose-1.4.6/django_nose/management/000077500000000000000000000000001335523113700203335ustar00rootroot00000000000000django-nose-1.4.6/django_nose/management/__init__.py000066400000000000000000000000501335523113700224370ustar00rootroot00000000000000"""django-nose management additions.""" django-nose-1.4.6/django_nose/management/commands/000077500000000000000000000000001335523113700221345ustar00rootroot00000000000000django-nose-1.4.6/django_nose/management/commands/__init__.py000066400000000000000000000000471335523113700242460ustar00rootroot00000000000000"""django-nose management commands.""" django-nose-1.4.6/django_nose/management/commands/test.py000066400000000000000000000011451335523113700234660ustar00rootroot00000000000000# coding: utf-8 """ Add extra options from the test runner to the ``test`` command. This enables browsing all the nose options from the command line. """ from __future__ import unicode_literals from django.conf import settings from django.core.management.commands.test import Command from django.test.utils import get_runner TestRunner = get_runner(settings) if hasattr(TestRunner, 'options'): extra_options = TestRunner.options else: extra_options = [] class Command(Command): """Implement the ``test`` command.""" option_list = getattr(Command, 'option_list', ()) + tuple(extra_options) django-nose-1.4.6/django_nose/plugin.py000066400000000000000000000244541335523113700201000ustar00rootroot00000000000000# coding: utf-8 """Included django-nose plugins.""" from __future__ import unicode_literals import sys from nose.plugins.base import Plugin from nose.suite import ContextSuite from django.test.testcases import TransactionTestCase, TestCase from django_nose.testcases import FastFixtureTestCase from django_nose.utils import process_tests, is_subclass_at_all class AlwaysOnPlugin(Plugin): """A base plugin that takes no options and is always enabled.""" def options(self, parser, env): """Avoid adding a ``--with`` option for this plugin. We don't have any options, and this plugin is always enabled, so we don't want to use superclass's ``options()`` method which would add a ``--with-*`` option. """ def configure(self, *args, **kw_args): """Configure and enable this plugin.""" super(AlwaysOnPlugin, self).configure(*args, **kw_args) self.enabled = True class ResultPlugin(AlwaysOnPlugin): """Captures the TestResult object for later inspection. nose doesn't return the full test result object from any of its runner methods. Pass an instance of this plugin to the TestProgram and use ``result`` after running the tests to get the TestResult object. """ name = 'result' def finalize(self, result): """Finalize test run by capturing the result.""" self.result = result class DjangoSetUpPlugin(AlwaysOnPlugin): """Configures Django to set up and tear down the environment. This allows coverage to report on all code imported and used during the initialization of the test runner. """ name = 'django setup' score = 150 def __init__(self, runner): """Initialize the plugin with the test runner.""" super(DjangoSetUpPlugin, self).__init__() self.runner = runner self.sys_stdout = sys.stdout def prepareTest(self, test): """Create the Django DB and model tables, and do other setup. This isn't done in begin() because that's too early--the DB has to be set up *after* the tests are imported so the model registry contains models defined in tests.py modules. Models are registered at declaration time by their metaclass. prepareTestRunner() might also have been a sane choice, except that, if some plugin returns something from it, none of the other ones get called. I'd rather not dink with scores if I don't have to. """ # What is this stdout switcheroo for? sys_stdout = sys.stdout sys.stdout = self.sys_stdout self.runner.setup_test_environment() self.old_names = self.runner.setup_databases() sys.stdout = sys_stdout def finalize(self, result): """Finalize test run by cleaning up databases and environment.""" self.runner.teardown_databases(self.old_names) self.runner.teardown_test_environment() class Bucketer(object): """Collect tests into buckets with similar setup requirements.""" def __init__(self): """Initialize the test buckets.""" # { (frozenset(['users.json']), True): # [ContextSuite(...), ContextSuite(...)] } self.buckets = {} # All the non-FastFixtureTestCase tests we saw, in the order they came # in: self.remainder = [] def add(self, test): """Add test into an initialization bucket. Tests are bucketed according to its set of fixtures and the value of its exempt_from_fixture_bundling attr. """ if is_subclass_at_all(test.context, FastFixtureTestCase): # We bucket even FFTCs that don't have any fixtures, but it # shouldn't matter. key = (frozenset(getattr(test.context, 'fixtures', [])), getattr(test.context, 'exempt_from_fixture_bundling', False)) self.buckets.setdefault(key, []).append(test) else: self.remainder.append(test) class TestReorderer(AlwaysOnPlugin): """Reorder tests for various reasons.""" name = 'django-nose-test-reorderer' def options(self, parser, env): """Add --with-fixture-bundling to options.""" super(TestReorderer, self).options(parser, env) # pointless parser.add_option('--with-fixture-bundling', action='store_true', dest='with_fixture_bundling', default=env.get('NOSE_WITH_FIXTURE_BUNDLING', False), help='Load a unique set of fixtures only once, even ' 'across test classes. ' '[NOSE_WITH_FIXTURE_BUNDLING]') def configure(self, options, conf): """Configure plugin, reading the with_fixture_bundling option.""" super(TestReorderer, self).configure(options, conf) self.should_bundle = options.with_fixture_bundling def _put_transaction_test_cases_last(self, test): """Reorder test suite so TransactionTestCase-based tests come last. Django has a weird design decision wherein TransactionTestCase doesn't clean up after itself. Instead, it resets the DB to a clean state only at the *beginning* of each test: https://docs.djangoproject.com/en/dev/topics/testing/?from=olddocs# django. test.TransactionTestCase. Thus, Django reorders tests so TransactionTestCases all come last. Here we do the same. "I think it's historical. We used to have doctests also, adding cleanup after each unit test wouldn't necessarily clean up after doctests, so you'd have to clean on entry to a test anyway." was once uttered on #django-dev. """ def filthiness(test): """Return a score of how messy a test leaves the environment. Django's TransactionTestCase doesn't clean up the DB on teardown, but it's hard to guess whether subclasses (other than TestCase) do. We will assume they don't, unless they have a ``cleans_up_after_itself`` attr set to True. This is reasonable because the odd behavior of TransactionTestCase is documented, so subclasses should by default be assumed to preserve it. Thus, things will get these comparands (and run in this order): * 1: TestCase subclasses. These clean up after themselves. * 1: TransactionTestCase subclasses with cleans_up_after_itself=True. These include FastFixtureTestCases. If you're using the FixtureBundlingPlugin, it will pull the FFTCs out, reorder them, and run them first of all. * 2: TransactionTestCase subclasses. These leave a mess. * 2: Anything else (including doctests, I hope). These don't care about the mess you left, because they don't hit the DB or, if they do, are responsible for ensuring that it's clean (as per https://docs.djangoproject.com/en/dev/topics/testing/?from= olddocs#writing-doctests) """ test_class = test.context if (is_subclass_at_all(test_class, TestCase) or (is_subclass_at_all(test_class, TransactionTestCase) and getattr(test_class, 'cleans_up_after_itself', False))): return 1 return 2 flattened = [] process_tests(test, flattened.append) flattened.sort(key=filthiness) return ContextSuite(flattened) def _bundle_fixtures(self, test): """Reorder tests to minimize fixture loading. I reorder FastFixtureTestCases so ones using identical sets of fixtures run adjacently. I then put attributes on them to advise them to not reload the fixtures for each class. This takes support.mozilla.com's suite from 123s down to 94s. FastFixtureTestCases are the only ones we care about, because nobody else, in practice, pays attention to the ``_fb`` advisory bits. We return those first, then any remaining tests in the order they were received. """ def suite_sorted_by_fixtures(suite): """Flatten and sort a tree of Suites by fixture. Add ``_fb_should_setup_fixtures`` and ``_fb_should_teardown_fixtures`` attrs to each test class to advise it whether to set up or tear down (respectively) the fixtures. Return a Suite. """ bucketer = Bucketer() process_tests(suite, bucketer.add) # Lay the bundles of common-fixture-having test classes end to end # in a single list so we can make a test suite out of them: flattened = [] for (key, fixture_bundle) in bucketer.buckets.items(): fixtures, is_exempt = key # Advise first and last test classes in each bundle to set up # and tear down fixtures and the rest not to: if fixtures and not is_exempt: # Ones with fixtures are sure to be classes, which means # they're sure to be ContextSuites with contexts. # First class with this set of fixtures sets up: first = fixture_bundle[0].context first._fb_should_setup_fixtures = True # Set all classes' 1..n should_setup to False: for cls in fixture_bundle[1:]: cls.context._fb_should_setup_fixtures = False # Last class tears down: last = fixture_bundle[-1].context last._fb_should_teardown_fixtures = True # Set all classes' 0..(n-1) should_teardown to False: for cls in fixture_bundle[:-1]: cls.context._fb_should_teardown_fixtures = False flattened.extend(fixture_bundle) flattened.extend(bucketer.remainder) return ContextSuite(flattened) return suite_sorted_by_fixtures(test) def prepareTest(self, test): """Reorder the tests.""" test = self._put_transaction_test_cases_last(test) if self.should_bundle: test = self._bundle_fixtures(test) return test django-nose-1.4.6/django_nose/runner.py000066400000000000000000000454511335523113700201130ustar00rootroot00000000000000# coding: utf-8 """Django test runner that invokes nose. You can use... :: NOSE_ARGS = ['list', 'of', 'args'] in settings.py for arguments that you want always passed to nose. """ from __future__ import print_function, unicode_literals import os import sys from importlib import import_module from optparse import NO_DEFAULT from types import MethodType from django import setup from django.apps import apps from django.conf import settings from django.core import exceptions from django.core.management.color import no_style from django.core.management.commands.loaddata import Command from django.db import connections, transaction, DEFAULT_DB_ALIAS from django.test.runner import DiscoverRunner from django_nose.plugin import DjangoSetUpPlugin, ResultPlugin, TestReorderer from django_nose.utils import uses_mysql import nose.core __all__ = ('BasicNoseRunner', 'NoseTestSuiteRunner') # This is a table of Django's "manage.py test" options which # correspond to nosetests options with a different name: OPTION_TRANSLATION = {'--failfast': '-x', '--nose-verbosity': '--verbosity'} def translate_option(opt): if '=' in opt: long_opt, value = opt.split('=', 1) return '%s=%s' % (translate_option(long_opt), value) return OPTION_TRANSLATION.get(opt, opt) def _get_plugins_from_settings(): plugins = (list(getattr(settings, 'NOSE_PLUGINS', [])) + ['django_nose.plugin.TestReorderer']) for plug_path in plugins: try: dot = plug_path.rindex('.') except ValueError: raise exceptions.ImproperlyConfigured( "%s isn't a Nose plugin module" % plug_path) p_mod, p_classname = plug_path[:dot], plug_path[dot + 1:] try: mod = import_module(p_mod) except ImportError as e: raise exceptions.ImproperlyConfigured( 'Error importing Nose plugin module %s: "%s"' % (p_mod, e)) try: p_class = getattr(mod, p_classname) except AttributeError: raise exceptions.ImproperlyConfigured( 'Nose plugin module "%s" does not define a "%s"' % (p_mod, p_classname)) yield p_class() class BaseRunner(DiscoverRunner): """Runner that translates nose optparse arguments to argparse. Django 1.8 and later uses argparse.ArgumentParser. Nose's optparse arguments need to be translated to this format, so that the Django command line parsing will pass. This parsing is (mostly) thrown out, and reassembled into command line arguments for nose to reparse. """ # Don't pass the following options to nosetests django_opts = [ '--noinput', '--liveserver', '-p', '--pattern', '--testrunner', '--settings', # 1.8 arguments '--keepdb', '--reverse', '--debug-sql', # 1.9 arguments '--parallel', # 1.10 arguments '--tag', '--exclude-tag', # 1.11 arguments '--debug-mode', ] # # For optparse -> argparse conversion # # Option strings to remove from Django options if found _argparse_remove_options = ( '-p', # Short arg for nose's --plugins, not Django's --patterns '-d', # Short arg for nose's --detailed-errors, not Django's # --debug-sql ) # Convert nose optparse options to argparse options _argparse_type = { 'int': int, 'float': float, 'complex': complex, 'string': str, 'choice': str, } # If optparse has a None argument, omit from call to add_argument _argparse_omit_if_none = ( 'action', 'nargs', 'const', 'default', 'type', 'choices', 'required', 'help', 'metavar', 'dest') # Always ignore these optparse arguments # Django will parse without calling the callback # nose will then reparse with the callback _argparse_callback_options = ( 'callback', 'callback_args', 'callback_kwargs') # Keep track of nose options with nargs=1 _has_nargs = set(['--verbosity']) @classmethod def add_arguments(cls, parser): """Convert nose's optparse arguments to argparse.""" super(BaseRunner, cls).add_arguments(parser) # Read optparse options for nose and plugins cfg_files = nose.core.all_config_files() manager = nose.core.DefaultPluginManager() config = nose.core.Config( env=os.environ, files=cfg_files, plugins=manager) config.plugins.addPlugins(list(_get_plugins_from_settings())) options = config.getParser()._get_all_options() # Gather existing option strings` django_options = set() for action in parser._actions: for override in cls._argparse_remove_options: if override in action.option_strings: # Emulate parser.conflict_handler='resolve' parser._handle_conflict_resolve( None, ((override, action),)) django_options.update(action.option_strings) # Process nose optparse options for option in options: # Gather options opt_long = option.get_opt_string() if option._short_opts: opt_short = option._short_opts[0] else: opt_short = None # Rename nose's --verbosity to --nose-verbosity if opt_long == '--verbosity': opt_long = '--nose-verbosity' # Skip any options also in Django options if opt_long in django_options: continue if opt_short and opt_short in django_options: opt_short = None # Convert optparse attributes to argparse attributes option_attrs = {} for attr in option.ATTRS: # Ignore callback options if attr in cls._argparse_callback_options: continue value = getattr(option, attr) if attr == 'default' and value == NO_DEFAULT: continue # Rename options for nose's --verbosity if opt_long == '--nose-verbosity': if attr == 'dest': value = 'nose_verbosity' elif attr == 'metavar': value = 'NOSE_VERBOSITY' # Omit arguments that are None, use default if attr in cls._argparse_omit_if_none and value is None: continue # Convert type from optparse string to argparse type if attr == 'type': value = cls._argparse_type[value] # Convert action='callback' to action='store' if attr == 'action' and value == 'callback': action = 'store' # Keep track of nargs=1 if attr == 'nargs': assert value == 1, ( 'argparse option nargs=%s is not supported' % value) cls._has_nargs.add(opt_long) if opt_short: cls._has_nargs.add(opt_short) # Pass converted attribute to optparse option option_attrs[attr] = value # Add the optparse argument if opt_short: parser.add_argument(opt_short, opt_long, **option_attrs) else: parser.add_argument(opt_long, **option_attrs) class BasicNoseRunner(BaseRunner): """Facade that implements a nose runner in the guise of a Django runner. You shouldn't have to use this directly unless the additions made by ``NoseTestSuiteRunner`` really bother you. They shouldn't, because they're all off by default. """ __test__ = False def run_suite(self, nose_argv): """Run the test suite.""" result_plugin = ResultPlugin() plugins_to_add = [DjangoSetUpPlugin(self), result_plugin, TestReorderer()] for plugin in _get_plugins_from_settings(): plugins_to_add.append(plugin) setup() nose.core.TestProgram(argv=nose_argv, exit=False, addplugins=plugins_to_add) return result_plugin.result def run_tests(self, test_labels, extra_tests=None): """ Run the unit tests for all the test names in the provided list. Test names specified may be file or module names, and may optionally indicate the test case to run by separating the module or file name from the test case name with a colon. Filenames may be relative or absolute. N.B.: The test_labels argument *MUST* be a sequence of strings, *NOT* just a string object. (Or you will be specifying tests for for each character in your string, and not the whole string. Examples: runner.run_tests( ('test.module',) ) runner.run_tests(['another.test:TestCase.test_method']) runner.run_tests(['a.test:TestCase']) runner.run_tests(['/path/to/test/file.py:test_function']) runner.run_tests( ('test.module', 'a.test:TestCase') ) Note: the extra_tests argument is currently ignored. You can run old non-nose code that uses it without totally breaking, but the extra tests will not be run. Maybe later. Returns the number of tests that failed. """ nose_argv = (['nosetests'] + list(test_labels)) if hasattr(settings, 'NOSE_ARGS'): nose_argv.extend(settings.NOSE_ARGS) # Recreate the arguments in a nose-compatible format arglist = sys.argv[1:] has_nargs = getattr(self, '_has_nargs', set(['--verbosity'])) while arglist: opt = arglist.pop(0) if not opt.startswith('-'): # Discard test labels continue if any(opt.startswith(d) for d in self.django_opts): # Discard options handled by Djangp continue trans_opt = translate_option(opt) nose_argv.append(trans_opt) if opt in has_nargs: # Handle arguments without an equals sign opt_value = arglist.pop(0) nose_argv.append(opt_value) # if --nose-verbosity was omitted, pass Django verbosity to nose if ('--verbosity' not in nose_argv and not any(opt.startswith('--verbosity=') for opt in nose_argv)): nose_argv.append('--verbosity=%s' % str(self.verbosity)) if self.verbosity >= 1: print(' '.join(nose_argv)) result = self.run_suite(nose_argv) # suite_result expects the suite as the first argument. Fake it. return self.suite_result({}, result) _old_handle = Command.handle def _foreign_key_ignoring_handle(self, *fixture_labels, **options): """Wrap the the stock loaddata to ignore foreign key checks. This allows loading circular references from fixtures, and is monkeypatched into place in setup_databases(). """ using = options.get('database', DEFAULT_DB_ALIAS) commit = options.get('commit', True) connection = connections[using] # MySQL stinks at loading circular references: if uses_mysql(connection): cursor = connection.cursor() cursor.execute('SET foreign_key_checks = 0') _old_handle(self, *fixture_labels, **options) if uses_mysql(connection): cursor = connection.cursor() cursor.execute('SET foreign_key_checks = 1') if commit: connection.close() def _skip_create_test_db(self, verbosity=1, autoclobber=False, serialize=True, keepdb=True): """``create_test_db`` implementation that skips both creation and flushing. The idea is to re-use the perfectly good test DB already created by an earlier test run, cutting the time spent before any tests run from 5-13s (depending on your I/O luck) down to 3. """ # Notice that the DB supports transactions. Originally, this was done in # the method this overrides. The confirm method was added in Django v1.3 # (https://code.djangoproject.com/ticket/12991) but removed in Django v1.5 # (https://code.djangoproject.com/ticket/17760). In Django v1.5 # supports_transactions is a cached property evaluated on access. if callable(getattr(self.connection.features, 'confirm', None)): # Django v1.3-4 self.connection.features.confirm() elif hasattr(self, "_rollback_works"): # Django v1.2 and lower can_rollback = self._rollback_works() self.connection.settings_dict['SUPPORTS_TRANSACTIONS'] = can_rollback return self._get_test_db_name() def _reusing_db(): """Return whether the ``REUSE_DB`` flag was passed.""" return os.getenv('REUSE_DB', 'false').lower() in ('true', '1') def _can_support_reuse_db(connection): """Return True if REUSE_DB is a sensible option for the backend.""" # Perhaps this is a SQLite in-memory DB. Those are created implicitly when # you try to connect to them, so our usual test doesn't work. return not connection.creation._get_test_db_name() == ':memory:' def _should_create_database(connection): """Return whether we should recreate the given DB. This is true if the DB doesn't exist or the REUSE_DB env var isn't truthy. """ # TODO: Notice when the Model classes change and return True. Worst case, # we can generate sqlall and hash it, though it's a bit slow (2 secs) and # hits the DB for no good reason. Until we find a faster way, I'm inclined # to keep making people explicitly saying REUSE_DB if they want to reuse # the DB. if not _can_support_reuse_db(connection): return True # Notice whether the DB exists, and create it if it doesn't: try: # Connections are cached by some backends, if other code has connected # to the database previously under a different database name the # cached connection will be used and no exception will be raised. # Avoiding this by closing connections and setting to null for connection in connections.all(): connection.close() connection.connection = None connection.cursor() except Exception: # TODO: Be more discerning but still DB agnostic. return True return not _reusing_db() def _mysql_reset_sequences(style, connection): """Return a SQL statements needed to reset Django tables.""" tables = connection.introspection.django_table_names(only_existing=True) flush_statements = connection.ops.sql_flush( style, tables, connection.introspection.sequence_list()) # connection.ops.sequence_reset_sql() is not implemented for MySQL, # and the base class just returns []. TODO: Implement it by pulling # the relevant bits out of sql_flush(). return [s for s in flush_statements if s.startswith('ALTER')] # Being overzealous and resetting the sequences on non-empty tables # like django_content_type seems to be fine in MySQL: adding a row # afterward does find the correct sequence number rather than # crashing into an existing row. class NoseTestSuiteRunner(BasicNoseRunner): """A runner that optionally skips DB creation. Monkeypatches connection.creation to let you skip creating databases if they already exist. Your tests will start up much faster. To opt into this behavior, set the environment variable ``REUSE_DB`` to "1" or "true" (case insensitive). """ def _get_models_for_connection(self, connection): """Return a list of models for a connection.""" tables = connection.introspection.get_table_list(connection.cursor()) return [m for m in apps.get_models() if m._meta.db_table in tables] def setup_databases(self): """Set up databases. Skip DB creation if requested and possible.""" for alias in connections: connection = connections[alias] creation = connection.creation test_db_name = creation._get_test_db_name() # Mess with the DB name so other things operate on a test DB # rather than the real one. This is done in create_test_db when # we don't monkeypatch it away with _skip_create_test_db. orig_db_name = connection.settings_dict['NAME'] connection.settings_dict['NAME'] = test_db_name if _should_create_database(connection): # We're not using _skip_create_test_db, so put the DB name # back: connection.settings_dict['NAME'] = orig_db_name # Since we replaced the connection with the test DB, closing # the connection will avoid pooling issues with SQLAlchemy. The # issue is trying to CREATE/DROP the test database using a # connection to a DB that was established with that test DB. # MySQLdb doesn't allow it, and SQLAlchemy attempts to reuse # the existing connection from its pool. connection.close() else: # Reset auto-increment sequences. Apparently, SUMO's tests are # horrid and coupled to certain numbers. cursor = connection.cursor() style = no_style() if uses_mysql(connection): reset_statements = _mysql_reset_sequences( style, connection) else: reset_statements = connection.ops.sequence_reset_sql( style, self._get_models_for_connection(connection)) if hasattr(transaction, "atomic"): with transaction.atomic(using=connection.alias): for reset_statement in reset_statements: cursor.execute(reset_statement) else: # Django < 1.6 for reset_statement in reset_statements: cursor.execute(reset_statement) transaction.commit_unless_managed(using=connection.alias) # Each connection has its own creation object, so this affects # only a single connection: creation.create_test_db = MethodType( _skip_create_test_db, creation) Command.handle = _foreign_key_ignoring_handle # With our class patch, does nothing but return some connection # objects: return super(NoseTestSuiteRunner, self).setup_databases() def teardown_databases(self, *args, **kwargs): """Leave those poor, reusable databases alone if REUSE_DB is true.""" if not _reusing_db(): return super(NoseTestSuiteRunner, self).teardown_databases( *args, **kwargs) # else skip tearing down the DB so we can reuse it next time django-nose-1.4.6/django_nose/testcases.py000066400000000000000000000160351335523113700205740ustar00rootroot00000000000000# coding: utf-8 """TestCases that enable extra django-nose functionality.""" from __future__ import unicode_literals from django import test from django.conf import settings from django.core import cache, mail from django.core.management import call_command from django.db import connections, DEFAULT_DB_ALIAS, transaction from django_nose.fixture_tables import tables_used_by_fixtures from django_nose.utils import uses_mysql __all__ = ('FastFixtureTestCase', ) class FastFixtureTestCase(test.TransactionTestCase): """Test case that loads fixtures once rather than once per test. Using this can save huge swaths of time while still preserving test isolation. Fixture data is loaded at class setup time, and the transaction is committed. Commit and rollback methods are then monkeypatched away (like in Django's standard TestCase), and each test is run. After each test, the monkeypatching is temporarily undone, and a rollback is issued, returning the DB content to the pristine fixture state. Finally, upon class teardown, the DB is restored to a post-syncdb-like state by deleting the contents of any table that had been touched by a fixture (keeping infrastructure tables like django_content_type and auth_permission intact). Note that this is like Django's TestCase, not its TransactionTestCase, in that you cannot do your own commits or rollbacks from within tests. For best speed, group tests using the same fixtures into as few classes as possible. Better still, don't do that, and instead use the fixture-bundling plugin from django-nose, which does it dynamically at test time. """ cleans_up_after_itself = True # This is the good kind of puppy. @classmethod def setUpClass(cls): """Turn on manual commits. Load and commit the fixtures.""" if not test.testcases.connections_support_transactions(): raise NotImplementedError('%s supports only DBs with transaction ' 'capabilities.' % cls.__name__) for db in cls._databases(): # These MUST be balanced with one leave_* each: transaction.enter_transaction_management(using=db) # Don't commit unless we say so: transaction.managed(True, using=db) cls._fixture_setup() @classmethod def tearDownClass(cls): """Truncate the world, and turn manual commit management back off.""" cls._fixture_teardown() for db in cls._databases(): # Finish off any transactions that may have happened in # tearDownClass in a child method. if transaction.is_dirty(using=db): transaction.commit(using=db) transaction.leave_transaction_management(using=db) @classmethod def _fixture_setup(cls): """Load fixture data, and commit.""" for db in cls._databases(): if (hasattr(cls, 'fixtures') and getattr(cls, '_fb_should_setup_fixtures', True)): # Iff the fixture-bundling test runner tells us we're the first # suite having these fixtures, set them up: call_command('loaddata', *cls.fixtures, **{'verbosity': 0, 'commit': False, 'database': db}) # No matter what, to preserve the effect of cursor start-up # statements... transaction.commit(using=db) @classmethod def _fixture_teardown(cls): """Empty (only) the tables we loaded fixtures into, then commit.""" if hasattr(cls, 'fixtures') and \ getattr(cls, '_fb_should_teardown_fixtures', True): # If the fixture-bundling test runner advises us that the next test # suite is going to reuse these fixtures, don't tear them down. for db in cls._databases(): tables = tables_used_by_fixtures(cls.fixtures, using=db) # TODO: Think about respecting _meta.db_tablespace, not just # db_table. if tables: connection = connections[db] cursor = connection.cursor() # TODO: Rather than assuming that anything added to by a # fixture can be emptied, remove only what the fixture # added. This would probably solve input.mozilla.com's # failures (since worked around) with Site objects; they # were loading additional Sites with a fixture, and then # the Django-provided example.com site was evaporating. if uses_mysql(connection): cursor.execute('SET FOREIGN_KEY_CHECKS=0') for table in tables: # Truncate implicitly commits. cursor.execute('TRUNCATE `%s`' % table) # TODO: necessary? cursor.execute('SET FOREIGN_KEY_CHECKS=1') else: for table in tables: cursor.execute('DELETE FROM %s' % table) transaction.commit(using=db) # cursor.close() # Should be unnecessary, since we committed # any environment-setup statements that come with opening a new # cursor when we committed the fixtures. def _pre_setup(self): """Disable transaction methods, and clear some globals.""" # Repeat stuff from TransactionTestCase, because I'm not calling its # _pre_setup, because that would load fixtures again. cache.cache.clear() settings.TEMPLATE_DEBUG = settings.DEBUG = False test.testcases.disable_transaction_methods() self.client = self.client_class() # self._fixture_setup() self._urlconf_setup() mail.outbox = [] # Clear site cache in case somebody's mutated Site objects and then # cached the mutated stuff: from django.contrib.sites.models import Site Site.objects.clear_cache() def _post_teardown(self): """Re-enable transaction methods, and roll back any changes. Rollback clears any DB changes made by the test so the original fixture data is again visible. """ # Rollback any mutations made by tests: test.testcases.restore_transaction_methods() for db in self._databases(): transaction.rollback(using=db) self._urlconf_teardown() # We do not need to close the connection here to prevent # http://code.djangoproject.com/ticket/7572, since we commit, not # rollback, the test fixtures and thus any cursor startup statements. # Don't call through to superclass, because that would call # _fixture_teardown() and close the connection. @classmethod def _databases(cls): if getattr(cls, 'multi_db', False): return connections else: return [DEFAULT_DB_ALIAS] django-nose-1.4.6/django_nose/tools.py000066400000000000000000000053551335523113700177410ustar00rootroot00000000000000# coding: utf-8 # vim: tabstop=4 expandtab autoindent shiftwidth=4 fileencoding=utf-8 """Provides Nose and Django test case assert functions.""" from __future__ import unicode_literals def _get_nose_vars(): """Collect assert_*, ok_, and eq_ from nose.tools.""" from nose import tools new_names = {} for t in dir(tools): if t.startswith('assert_') or t in ('ok_', 'eq_'): new_names[t] = getattr(tools, t) return new_names for _name, _value in _get_nose_vars().items(): vars()[_name] = _value def _get_django_vars(): """Collect assert_* methods from Django's TransactionTestCase.""" import re from django.test.testcases import TransactionTestCase camelcase = re.compile('([a-z][A-Z]|[A-Z][a-z])') def insert_underscore(m): """Insert an appropriate underscore into the name.""" a, b = m.group(0) if b.islower(): return '_{}{}'.format(a, b) else: return '{}_{}'.format(a, b) def pep8(name): """Replace camelcase name with PEP8 equivalent.""" return str(camelcase.sub(insert_underscore, name).lower()) class Dummy(TransactionTestCase): """A dummy test case for gathering current assertion helpers.""" def nop(): """Do nothing, dummy test to get an initialized test case.""" pass dummy_test = Dummy('nop') new_names = {} for assert_name in [at for at in dir(dummy_test) if at.startswith('assert') and '_' not in at]: pepd = pep8(assert_name) new_names[pepd] = getattr(dummy_test, assert_name) return new_names for _name, _value in _get_django_vars().items(): vars()[_name] = _value # # Additional assertions # def assert_code(response, status_code, msg_prefix=''): """Assert the response was returned with the given status code.""" if msg_prefix: msg_prefix = '%s: ' % msg_prefix assert response.status_code == status_code, \ 'Response code was %d (expected %d)' % ( response.status_code, status_code) def assert_ok(response, msg_prefix=''): """Assert the response was returned with status 200 (OK).""" return assert_code(response, 200, msg_prefix=msg_prefix) def assert_mail_count(count, msg=None): """Assert the number of emails sent. The message here tends to be long, so allow for replacing the whole thing instead of prefixing. """ from django.core import mail if msg is None: msg = ', '.join([e.subject for e in mail.outbox]) msg = '%d != %d %s' % (len(mail.outbox), count, msg) # assert_equals is dynamicaly added above. F821 is undefined name error assert_equals(len(mail.outbox), count, msg) # noqa: F821 django-nose-1.4.6/django_nose/utils.py000066400000000000000000000037421335523113700177370ustar00rootroot00000000000000# coding: utf-8 """django-nose utility methods.""" from __future__ import unicode_literals def process_tests(suite, process): """Find and process the suite with setup/teardown methods. Given a nested disaster of [Lazy]Suites, traverse to the first level that has setup or teardown, and do something to them. If we were to traverse all the way to the leaves (the Tests) indiscriminately and return them, when the runner later calls them, they'd run without reference to the suite that contained them, so they'd miss their class-, module-, and package-wide setup and teardown routines. The nested suites form basically a double-linked tree, and suites will call up to their containing suites to run their setups and teardowns, but it would be hubris to assume that something you saw fit to setup or teardown at the module level is less costly to repeat than DB fixtures. Also, those sorts of setups and teardowns are extremely rare in our code. Thus, we limit the granularity of bucketing to the first level that has setups or teardowns. :arg process: The thing to call once we get to a leaf or a test with setup or teardown """ if (not hasattr(suite, '_tests') or (hasattr(suite, 'hasFixtures') and suite.hasFixtures())): # We hit a Test or something with setup, so do the thing. (Note that # "fixtures" here means setup or teardown routines, not Django # fixtures.) process(suite) else: for t in suite._tests: process_tests(t, process) def is_subclass_at_all(cls, class_info): """Return whether ``cls`` is a subclass of ``class_info``. Even if ``cls`` is not a class, don't crash. Return False instead. """ try: return issubclass(cls, class_info) except TypeError: return False def uses_mysql(connection): """Return whether the connection represents a MySQL DB.""" return 'mysql' in connection.settings_dict['ENGINE'] django-nose-1.4.6/docs/000077500000000000000000000000001335523113700146615ustar00rootroot00000000000000django-nose-1.4.6/docs/Makefile000066400000000000000000000152011335523113700163200ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -W -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-nose.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-nose.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/django-nose" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-nose" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." django-nose-1.4.6/docs/_static/000077500000000000000000000000001335523113700163075ustar00rootroot00000000000000django-nose-1.4.6/docs/_static/.keep000066400000000000000000000000001335523113700172220ustar00rootroot00000000000000django-nose-1.4.6/docs/authors.rst000066400000000000000000000000341335523113700170750ustar00rootroot00000000000000.. include:: ../AUTHORS.rst django-nose-1.4.6/docs/changelog.rst000066400000000000000000000000361335523113700173410ustar00rootroot00000000000000.. include:: ../changelog.rst django-nose-1.4.6/docs/conf.py000066400000000000000000000202671335523113700161670ustar00rootroot00000000000000# -*- coding: utf-8 -*- """django-nose build configuration file. Created by sphinx-quickstart on Mon Jul 21 13:24:51 2014. This file is execfile()d with the current directory set to its containing dir. Note that not all possible configuration values are present in this autogenerated file. All configuration values have a default; values that are commented out serve to show the default. """ from __future__ import unicode_literals from datetime import date import sys import os cwd = os.getcwd() parent = os.path.dirname(cwd) sys.path.append(parent) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings") from django_nose import __version__ # flake8: noqa # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'django-nose' copyright = ( '2010-%d, Jeff Balogh and the django-nose team.' % date.today().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'django-nose-doc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'django-nose.tex', 'django-nose Documentation', 'Jeff Balogh and the django-nose team', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'django-nose', 'django-nose Documentation', ['Jeff Balogh', 'the django-nose team'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'django-nose', 'django-nose Documentation', 'Jeff Balogh and the django-nose team', 'django-nose', 'Makes your Django tests simple and snappy') ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False django-nose-1.4.6/docs/contributing.rst000066400000000000000000000000401335523113700201140ustar00rootroot00000000000000.. include:: ../CONTRIBUTING.rstdjango-nose-1.4.6/docs/index.rst000066400000000000000000000005621335523113700165250ustar00rootroot00000000000000.. django-nose documentation master file =========== django-nose =========== .. include:: ../README.rst :start-after: .. Omit badges from docs Contents -------- .. toctree:: :maxdepth: 2 installation usage upgrading contributing authors changelog Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` * :ref:`search` django-nose-1.4.6/docs/installation.rst000066400000000000000000000011711335523113700201140ustar00rootroot00000000000000Installation ------------ You can get django-nose from PyPI with... : .. code-block:: shell $ pip install django-nose The development version can be installed with... : .. code-block:: shell $ pip install -e git://github.com/django-nose/django-nose.git#egg=django-nose Since django-nose extends Django's built-in test command, you should add it to your ``INSTALLED_APPS`` in ``settings.py``: .. code-block:: python INSTALLED_APPS = ( ... 'django_nose', ... ) Then set ``TEST_RUNNER`` in ``settings.py``: .. code-block:: python TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' django-nose-1.4.6/docs/make.bat000066400000000000000000000145051335523113700162730ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-nose.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-nose.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end django-nose-1.4.6/docs/upgrading.rst000066400000000000000000000041301335523113700173710ustar00rootroot00000000000000Upgrading Django ================ Upgrading from Django <= 1.3 to Django 1.4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In versions of Django < 1.4 the project folder was in fact a python package as well (note the __init__.py in your project root). In Django 1.4, there is no such file and thus the project is not a python module. **When you upgrade your Django project to the Django 1.4 layout, you need to remove the __init__.py file in the root of your project (and move any python files that reside there other than the manage.py) otherwise you will get a `ImportError: No module named urls` exception.** This happens because Nose will intelligently try to populate your sys.path, and in this particular case includes your parent directory if your project has a __init__.py file (see: https://github.com/nose-devs/nose/blob/release_1.1.2/nose/importer.py#L134). This means that even though you have set up your directory structure properly and set your `ROOT_URLCONF='my_project.urls'` to match the new structure, when running django-nose's test runner it will try to find your urls.py file in `'my_project.my_project.urls'`. Upgrading from Django < 1.2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Django 1.2 switches to a `class-based test runner`_. To use django-nose with Django 1.2, change your ``TEST_RUNNER`` from ``django_nose.run_tests`` to ``django_nose.NoseTestSuiteRunner``. ``django_nose.run_tests`` will continue to work in Django 1.2 but will raise a warning. In Django 1.3, it will stop working. If you were using ``django_nose.run_gis_tests``, you should also switch to ``django_nose.NoseTestSuiteRunner`` and use one of the `spatial backends`_ in your ``DATABASES`` settings. .. _class-based test runner: http://docs.djangoproject.com/en/dev/releases/1.2/#function-based-test-runners .. _spatial backends: http://docs.djangoproject.com/en/dev/ref/contrib/gis/db-api/#id1 Django 1.1 ~~~~~~~~~~ If you want to use django-nose with Django 1.1, use https://github.com/django-nose/django-nose/tree/django-1.1 or http://pypi.python.org/pypi/django-nose/0.0.3. Django 1.0 ~~~~~~~~~~ django-nose does not support Django 1.0. django-nose-1.4.6/docs/usage.rst000066400000000000000000000200321335523113700165140ustar00rootroot00000000000000Usage ===== The day-to-day use of django-nose is mostly transparent; just run ``./manage.py test`` as usual. See ``./manage.py help test`` for all the options nose provides, and look to the `nose docs`_ for more help with nose. .. _nose docs: https://nose.readthedocs.io/en/latest/ Enabling Database Reuse ----------------------- .. warning:: There are several `open issues `_ with this feature, including `reports of data loss `_. You can save several seconds at the beginning and end of your test suite by reusing the test database from the last run. To do this, set the environment variable ``REUSE_DB`` to 1:: REUSE_DB=1 ./manage.py test The one new wrinkle is that, whenever your DB schema changes, you should leave the flag off the next time you run tests. This will cue the test runner to reinitialize the test database. Also, REUSE_DB is not compatible with TransactionTestCases that leave junk in the DB, so be sure to make your TransactionTestCases hygienic (see below) if you want to use it. Enabling Fast Fixtures ---------------------- .. warning:: There are several `known issues `_ with this feature. django-nose includes a fixture bundler which drastically speeds up your tests by eliminating redundant setup of Django test fixtures. To use it... 1. Subclass ``django_nose.FastFixtureTestCase`` instead of ``django.test.TestCase``. (I like to import it ``as TestCase`` in my project's ``tests/__init__.py`` and then import it from there into my actual tests. Then it's easy to sub the base class in and out.) This alone will cause fixtures to load once per class rather than once per test. 2. Activate fixture bundling by passing the ``--with-fixture-bundling`` option to ``./manage.py test``. This loads each unique set of fixtures only once, even across class, module, and app boundaries. How Fixture Bundling Works ~~~~~~~~~~~~~~~~~~~~~~~~~~ The fixture bundler reorders your test classes so that ones with identical sets of fixtures run adjacently. It then advises the first of each series to load the fixtures once for all of them (and the remaining ones not to bother). It also advises the last to tear them down. Depending on the size and repetition of your fixtures, you can expect a 25% to 50% speed increase. Incidentally, the author prefers to avoid Django fixtures, as they encourage irrelevant coupling between tests and make tests harder to comprehend and modify. For future tests, it is better to use the "model maker" pattern, creating DB objects programmatically. This way, tests avoid setup they don't need, and there is a clearer tie between a test and the exact state it requires. The fixture bundler is intended to make existing tests, which have already committed to fixtures, more tolerable. Troubleshooting ~~~~~~~~~~~~~~~ If using ``--with-fixture-bundling`` causes test failures, it likely indicates an order dependency between some of your tests. Here are the most frequent sources of state leakage we have encountered: * Locale activation, which is maintained in a threadlocal variable. Be sure to reset your locale selection between tests. * memcached contents. Be sure to flush between tests. Many test superclasses do this automatically. It's also possible that you have ``post_save`` signal handlers which create additional database rows while loading the fixtures. ``FastFixtureTestCase`` isn't yet smart enough to notice this and clean up after it, so you'll have to go back to plain old ``TestCase`` for now. Exempting A Class From Bundling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some unusual cases, it is desirable to exempt a test class from fixture bundling, forcing it to set up and tear down its fixtures at the class boundaries. For example, we might have a ``TestCase`` subclass which sets up some state outside the DB in ``setUpClass`` and tears it down in ``tearDownClass``, and it might not be possible to adapt those routines to heed the advice of the fixture bundler. In such a case, simply set the ``exempt_from_fixture_bundling`` attribute of the test class to ``True``. Speedy Hygienic TransactionTestCases ------------------------------------ Unlike the stock Django test runner, django-nose lets you write custom TransactionTestCase subclasses which expect to start with an unmarred DB, saving an entire DB flush per test. Background ~~~~~~~~~~ The default Django TransactionTestCase class `can leave the DB in an unclean state`_ when it's done. To compensate, TransactionTestCase does a time-consuming flush of the DB *before* each test to ensure it begins with a clean slate. Django's stock test runner then runs TransactionTestCases last so they don't wreck the environment for better-behaved tests. django-nose replicates this behavior. Escaping the Grime ~~~~~~~~~~~~~~~~~~ Some people, however, have made subclasses of TransactionTestCase that clean up after themselves (and can do so efficiently, since they know what they've changed). Like TestCase, these may assume they start with a clean DB. However, any TransactionTestCases that run before them and leave a mess could cause them to fail spuriously. django-nose offers to fix this. If you include a special attribute on your well-behaved TransactionTestCase... :: class MyNiceTestCase(TransactionTestCase): cleans_up_after_itself = True ...django-nose will run it before any of those nasty, trash-spewing test cases. You can thus enjoy a big speed boost any time you make a TransactionTestCase clean up after itself: skipping a whole DB flush before every test. With a large schema, this can save minutes of IO. django-nose's own FastFixtureTestCase uses this feature, even though it ultimately acts more like a TestCase than a TransactionTestCase. .. _can leave the DB in an unclean state: https://docs.djangoproject.com/en/1.4/topics/testing/#django.test.TransactionTestCase Test-Only Models ---------------- If you have a model that is used only by tests (for example, to test an abstract model base class), you can put it in any file that's imported in the course of loading tests. For example, if the tests that need it are in ``test_models.py``, you can put the model in there, too. django-nose will make sure its DB table gets created. Assertions ---------- ``django-nose.tools`` provides pep8 versions of Django's TestCase asserts and some of its own as functions. :: assert_redirects(response, expected_url, status_code=302, target_status_code=200, host=None, msg_prefix='') assert_contains(response, text, count=None, status_code=200, msg_prefix='') assert_not_contains(response, text, count=None, status_code=200, msg_prefix='') assert_form_error(response, form, field, errors, msg_prefix='') assert_template_used(response, template_name, msg_prefix='') assert_template_not_used(response, template_name, msg_prefix='') assert_queryset_equal(qs, values, transform=repr) assert_num_queries(num, func=None, *args, **kwargs) assert_code(response, status_code, msg_prefix='') assert_ok(response, msg_prefix='') assert_mail_count(count, msg=None) Always Passing The Same Options ------------------------------- To always set the same command line options you can use a `nose.cfg or setup.cfg`_ (as usual) or you can specify them in settings.py like this:: NOSE_ARGS = ['--failed', '--stop'] .. _nose.cfg or setup.cfg: https://nose.readthedocs.io/en/latest/usage.html#configuration Custom Plugins -------------- If you need to `make custom plugins`_, you can define each plugin class somewhere within your app and load them from settings.py like this:: NOSE_PLUGINS = [ 'yourapp.tests.plugins.SystematicDysfunctioner', # ... ] Just like middleware or anything else, each string must be a dot-separated, importable path to an actual class. Each plugin class will be instantiated and added to the Nose test runner. .. _make custom plugins: https://nose.readthedocs.io/en/latest/plugins.html#writing-plugins django-nose-1.4.6/manage.py000077500000000000000000000004661335523113700155440ustar00rootroot00000000000000#!/usr/bin/env python """Django command-line utility for administrative tasks.""" import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-nose-1.4.6/requirements-rtd.txt000066400000000000000000000002251335523113700200030ustar00rootroot00000000000000# Requirements for ReadTheDocs # Must be set in the RTD Admin, at: # https://readthedocs.org/dashboard/django-nose/advanced/ Django>=1.10,<1.11 nose django-nose-1.4.6/requirements.txt000066400000000000000000000010311335523113700172100ustar00rootroot00000000000000# # Requirements for running django-nose testapp # # Latest Django Django>=2.0,<2.1 # This project -e . # Load database config from environment dj-database-url==0.4.2 # Packaging wheel==0.29.0 twine==1.9.1 # PEP8, PEP257, and static analysis flake8==3.4.1 flake8-docstrings==1.1.0 # Code coverage coverage==4.4.1 # Documentation Sphinx==1.6.3 # Packaging Linters check-manifest==0.35 pyroma==2.2 # Multi-env test runner tox==2.7.0 # Better interactive debugging gnureadline==6.3.3 ipython==5.4.1 ipdb==0.10.3 ipdbplugin==1.4.5 django-nose-1.4.6/runtests.sh000077500000000000000000000105201335523113700161550ustar00rootroot00000000000000#!/bin/bash # Parse command line VERBOSE=0 HELP=0 ERR=0 NOINPUT="" while [[ $# > 0 ]] do key="$1" case $key in -h|--help) HELP=1 ;; -v|--verbose) VERBOSE=1 ;; --noinput) NOINPUT="--noinput" ;; *) echo "Unknown option '$key'" ERR=1 HELP=1 ;; esac shift done if [ $HELP -eq 1 ] then echo "$0 [-vh] - Run django-nose integration tests." echo " -v/--verbose - Print output of test commands." echo " -h/--help - Print this help message." exit $ERR fi export PYTHONPATH=. HAS_HOTSHOT=$(python -c "\ try: import hotshot except ImportError: print('0') else: print('1') ") reset_env() { unset TEST_RUNNER unset NOSE_PLUGINS unset REUSE_DB } echo_output() { STDOUT=$1 STDERR=$2 if [ $VERBOSE -ne 1 ] then echo "stdout" echo "======" cat $STDOUT echo echo "stderr" echo "======" cat $STDERR fi rm $STDOUT $STDERR } django_test() { COMMAND=$1 TEST_COUNT=$2 DESCRIPTION=$3 CAN_FAIL=${4:-0} if [ $VERBOSE -eq 1 ] then echo "================" echo "Django settings:" ./manage.py diffsettings echo "================" fi if [ -n "$COVERAGE" ] then TEST="coverage run -p $COMMAND" else TEST="$COMMAND" fi # Temp files on Linux / OSX TMP_OUT=`mktemp 2>/dev/null || mktemp -t 'django-nose-runtests'` TMP_ERR=`mktemp 2>/dev/null || mktemp -t 'django-nose-runtests'` RETURN=0 if [ $VERBOSE -eq 1 ] then echo $TEST $TEST > >(tee $TMP_OUT) 2> >(tee $TMP_ERR >&2) else $TEST >$TMP_OUT 2>$TMP_ERR fi if [ $? -gt 0 ] then echo "FAIL (test failure): $DESCRIPTION" echo_output $TMP_OUT $TMP_ERR if [ "$CAN_FAIL" == "0" ] then exit 1 else return fi fi OUTPUT=`cat $TMP_OUT $TMP_ERR` echo $OUTPUT | grep "Ran $TEST_COUNT test" > /dev/null if [ $? -gt 0 ] then echo "FAIL (count!=$TEST_COUNT): $DESCRIPTION" echo_output $TMP_OUT $TMP_ERR if [ "$CAN_FAIL" == "0" ] then exit 1 else return fi else echo "PASS (count==$TEST_COUNT): $DESCRIPTION" fi rm $TMP_OUT $TMP_ERR # Check that we're hijacking the help correctly. $TEST --help 2>&1 | grep 'NOSE_DETAILED_ERRORS' > /dev/null if [ $? -gt 0 ] then echo "FAIL (--help): $DESCRIPTION" if [ "$CAN_FAIL" == 0 ] then exit 1; else return fi else echo "PASS ( --help): $DESCRIPTION" fi } TESTAPP_COUNT=6 reset_env django_test "./manage.py test $NOINPUT" $TESTAPP_COUNT 'normal settings' reset_env export TEST_RUNNER="django_nose.NoseTestSuiteRunner" django_test "./manage.py test $NOINPUT" $TESTAPP_COUNT 'test runner from environment' reset_env django_test "testapp/runtests.py testapp.test_only_this" 1 'via run_tests API' reset_env export NOSE_PLUGINS="testapp.plugins.SanityCheckPlugin" django_test "./manage.py test testapp/plugin_t $NOINPUT" 1 'with plugins' reset_env django_test "./manage.py test unittests $NOINPUT" 4 'unittests' reset_env django_test "./manage.py test unittests --verbosity 1 $NOINPUT" 4 'argument option without equals' reset_env django_test "./manage.py test unittests --nose-verbosity=2 $NOINPUT" 4 'argument with equals' reset_env django_test "./manage.py test unittests --testrunner=testapp.custom_runner.CustomNoseTestSuiteRunner $NOINPUT" 4 'unittests with testrunner' reset_env django_test "./manage.py test unittests --attr special $NOINPUT" 1 'select by attribute' reset_env export REUSE_DB=1 # For the many issues with REUSE_DB=1, see: # https://github.com/django-nose/django-nose/milestones/Fix%20REUSE_DB=1 django_test "./manage.py test $NOINPUT" $TESTAPP_COUNT 'with REUSE_DB=1, call #1' 'can fail' django_test "./manage.py test $NOINPUT" $TESTAPP_COUNT 'with REUSE_DB=1, call #2' 'can fail' if [ "$HAS_HOTSHOT" = "1" ] then # Python 3 doesn't support the hotshot profiler. See nose#842. reset_env django_test "./manage.py test $NOINPUT --with-profile --profile-restrict less_output" $TESTAPP_COUNT 'with profile plugin' fi django-nose-1.4.6/setup.cfg000066400000000000000000000003531335523113700155530ustar00rootroot00000000000000[check-manifest] ignore = .travis.yml tox.ini [coverage:run] branch = True source = django_nose,testapp,unittests [coverage:report] omit = testapp/migrations/*.py [flake8] exclude = .tox/*,build/* [wheel] universal = 1 django-nose-1.4.6/setup.py000066400000000000000000000062211335523113700154440ustar00rootroot00000000000000"""django-nose packaging.""" from __future__ import unicode_literals import os from codecs import open from setuptools import setup, find_packages def get_long_description(title): """Create the long_description from other files.""" ROOT = os.path.abspath(os.path.dirname(__file__)) readme = open(os.path.join(ROOT, 'README.rst'), 'r', 'utf8').read() body_tag = ".. Omit badges from docs" readme_body_start = readme.index(body_tag) assert readme_body_start readme_body = readme[readme_body_start + len(body_tag):] changelog = open(os.path.join(ROOT, 'changelog.rst'), 'r', 'utf8').read() old_tag = ".. Omit older changes from package" changelog_body_end = changelog.index(old_tag) assert changelog_body_end changelog_body = changelog[:changelog_body_end] bars = '=' * len(title) long_description = """ %(bars)s %(title)s %(bars)s %(readme_body)s %(changelog_body)s _(Older changes can be found in the full documentation)._ """ % locals() return long_description setup( name='django-nose', version='1.4.6', description='Makes your Django tests simple and snappy', long_description=get_long_description('django-nose'), author='Jeff Balogh', author_email='me@jeffbalogh.org', maintainer='Erik Rose', maintainer_email='erikrose@grinchcentral.com', url='http://github.com/django-nose/django-nose', license='BSD', packages=find_packages(exclude=['testapp', 'testapp/*']), include_package_data=True, zip_safe=False, install_requires=['nose>=1.2.1'], test_suite='testapp.runtests.runtests', # This blows up tox runs that install django-nose into a virtualenv, # because it causes Nose to import django_nose.runner before the Django # settings are initialized, leading to a mess of errors. There's no reason # we need FixtureBundlingPlugin declared as an entrypoint anyway, since you # need to be using django-nose to find the it useful, and django-nose knows # about it intrinsically. # entry_points=""" # [nose.plugins.0.10] # fixture_bundler = django_nose.fixture_bundling:FixtureBundlingPlugin # """, keywords='django nose django-nose', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Framework :: Django :: 1.8', 'Framework :: Django :: 1.9', 'Framework :: Django :: 1.10', 'Framework :: Django :: 1.11', 'Framework :: Django :: 2.0', 'Framework :: Django :: 2.1', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Topic :: Software Development :: Testing' ] ) django-nose-1.4.6/testapp/000077500000000000000000000000001335523113700154115ustar00rootroot00000000000000django-nose-1.4.6/testapp/__init__.py000066400000000000000000000000711335523113700175200ustar00rootroot00000000000000"""Sample Django application for django-nose testing.""" django-nose-1.4.6/testapp/custom_runner.py000066400000000000000000000003121335523113700206620ustar00rootroot00000000000000"""Custom runner to test overriding runner.""" from django_nose import NoseTestSuiteRunner class CustomNoseTestSuiteRunner(NoseTestSuiteRunner): """Custom test runner, to test overring runner.""" django-nose-1.4.6/testapp/fixtures/000077500000000000000000000000001335523113700172625ustar00rootroot00000000000000django-nose-1.4.6/testapp/fixtures/testdata.json000066400000000000000000000004431335523113700217670ustar00rootroot00000000000000[ { "pk": 1, "model": "testapp.question", "fields": { "question_text": "What is your favorite color?", "pub_date": "1975-04-09T00:00:00" } }, { "pk": 1, "model": "testapp.choice", "fields": { "choice_text": "Blue.", "question": 1, "votes": 3 } } ] django-nose-1.4.6/testapp/migrations/000077500000000000000000000000001335523113700175655ustar00rootroot00000000000000django-nose-1.4.6/testapp/migrations/0001_initial.py000066400000000000000000000024321335523113700222310ustar00rootroot00000000000000# -*- coding: utf-8 -*- # flake8: noqa from __future__ import unicode_literals from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Choice', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('choice_text', models.CharField(max_length=200)), ('votes', models.IntegerField(default=0)), ], options={ }, bases=(models.Model,), ), migrations.CreateModel( name='Question', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('question_text', models.CharField(max_length=200)), ('pub_date', models.DateTimeField(verbose_name=b'date published')), ], options={ }, bases=(models.Model,), ), migrations.AddField( model_name='choice', name='question', field=models.ForeignKey(to='testapp.Question', on_delete=models.CASCADE), preserve_default=True, ), ] django-nose-1.4.6/testapp/migrations/__init__.py000066400000000000000000000000361335523113700216750ustar00rootroot00000000000000"""Django 1.7+ migrations.""" django-nose-1.4.6/testapp/models.py000066400000000000000000000013611335523113700172470ustar00rootroot00000000000000"""Models for django-nose test application. Based on the Django tutorial: https://docs.djangoproject.com/en/1.8/intro/tutorial01/ """ from django.db import models class Question(models.Model): """A poll question.""" question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): """Return string representation.""" return self.question_text class Choice(models.Model): """A poll answer.""" question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): """Return string representation.""" return self.choice_text django-nose-1.4.6/testapp/plugin_t/000077500000000000000000000000001335523113700172325ustar00rootroot00000000000000django-nose-1.4.6/testapp/plugin_t/test_with_plugins.py000066400000000000000000000003201335523113700233520ustar00rootroot00000000000000"""Test loading of additional plugins.""" from nose.tools import eq_ def test_one(): """Test that the test plugin was initialized.""" from testapp import plugins eq_(plugins.plugin_began, True) django-nose-1.4.6/testapp/plugins.py000066400000000000000000000007411335523113700174460ustar00rootroot00000000000000"""Additional plugins for testing plugins.""" from nose.plugins import Plugin plugin_began = False class SanityCheckPlugin(Plugin): """Test plugin that registers that it ran.""" enabled = True def options(self, parser, env): """Register commandline options.""" def configure(self, options, conf): """Configure plugin.""" def begin(self): """Flag that the plugin was run.""" global plugin_began plugin_began = True django-nose-1.4.6/testapp/runtests.py000077500000000000000000000012171335523113700176560ustar00rootroot00000000000000#!/usr/bin/env python """Configure enough Django to run the test suite.""" import sys from django.conf import settings if not settings.configured: settings.configure( DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3'}}, INSTALLED_APPS=['django_nose'], MIDDLEWARE_CLASSES=[], ) def runtests(*test_labels): """Run the selected tests, or all tests if none selected.""" from django_nose import NoseTestSuiteRunner runner = NoseTestSuiteRunner(verbosity=1, interactive=True) failures = runner.run_tests(test_labels) sys.exit(failures) if __name__ == '__main__': runtests(*sys.argv[1:]) django-nose-1.4.6/testapp/settings.py000066400000000000000000000021351335523113700176240ustar00rootroot00000000000000""" Django settings for testing django-nose. Configuration is overriden by environment variables: DATABASE_URL - See https://github.com/kennethreitz/dj-database-url USE_SOUTH - Set to 1 to include South in INSTALLED_APPS TEST_RUNNER - Dotted path of test runner to use (can also use --test-runner) NOSE_PLUGINS - Comma-separated list of plugins to add """ from __future__ import print_function from os import environ, path import dj_database_url BASE_DIR = path.dirname(path.dirname(__file__)) def rel_path(*subpaths): """Construct the full path given a relative path.""" return path.join(BASE_DIR, *subpaths) DATABASES = { 'default': dj_database_url.config( default='sqlite:///' + rel_path('testapp.sqlite3')) } MIDDLEWARE_CLASSES = () INSTALLED_APPS = [ 'django_nose', 'testapp', ] raw_test_runner = environ.get('TEST_RUNNER') if raw_test_runner: TEST_RUNNER = raw_test_runner else: TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' raw_plugins = environ.get('NOSE_PLUGINS') if raw_plugins: NOSE_PLUGINS = raw_plugins.split(',') SECRET_KEY = 'ssshhhh' django-nose-1.4.6/testapp/test_for_nose.py000066400000000000000000000002141335523113700206310ustar00rootroot00000000000000"""Django's test runner won't find this, but nose will.""" def test_addition(): """Test some advanced maths.""" assert 1 + 1 == 2 django-nose-1.4.6/testapp/test_only_this.py000066400000000000000000000002231335523113700210270ustar00rootroot00000000000000"""Django's test runner won't find this, but nose will.""" def test_multiplication(): """Check some advanced maths.""" assert 2 * 2 == 4 django-nose-1.4.6/testapp/tests.py000066400000000000000000000033561335523113700171340ustar00rootroot00000000000000"""Django model tests.""" from datetime import datetime from django.test import TestCase, TransactionTestCase from testapp.models import Question, Choice class NoDatabaseTestCase(TestCase): """Tests that don't read or write to the database.""" def test_question_str(self): """Test Question.__str__ method.""" question = Question(question_text="What is your name?") self.assertEqual("What is your name?", str(question)) def test_choice_str(self): """Test Choice.__str__ method.""" choice = Choice(choice_text='My name is Sir Lancelot of Camelot.') self.assertEqual('My name is Sir Lancelot of Camelot.', str(choice)) class UsesDatabaseTestCase(TestCase): """Tests that read and write to the database.""" def test_question(self): """Test that votes is initialized to 0.""" question = Question.objects.create( question_text="What is your quest?", pub_date=datetime(1975, 4, 9)) Choice.objects.create( question=question, choice_text="To seek the Holy Grail.") self.assertTrue(question.choice_set.exists()) the_choice = question.choice_set.get() self.assertEqual(0, the_choice.votes) class UsesFixtureTestCase(TransactionTestCase): """Tests that use a test fixture.""" fixtures = ["testdata.json"] def test_fixture_loaded(self): """Test that fixture was loaded.""" question = Question.objects.get() self.assertEqual( 'What is your favorite color?', question.question_text) self.assertEqual(datetime(1975, 4, 9), question.pub_date) choice = question.choice_set.get() self.assertEqual("Blue.", choice.choice_text) self.assertEqual(3, choice.votes) django-nose-1.4.6/tox.ini000066400000000000000000000021041335523113700152410ustar00rootroot00000000000000[tox] envlist = py{27,34,35}-django-{18,19,110}{,-postgres,-mysql} py{27,34,35,36}-django-111{,-postgres,-mysql} py{34,35,36,37}-django-20{,-postgres,-mysql} py{35,36,37}-django-{21,master}{,-postgres,-mysql} flake8 docs skip_missing_interpreters = True [testenv] passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERAGE RUNTEST_ARGS DATABASE_URL commands = ./runtests.sh {env:RUNTEST_ARGS:} - coverage combine deps = coveralls dj-database-url django-18: Django>=1.8,<1.9 django-19: Django>=1.9,<1.10 django-110: Django>=1.10,<1.11 django-111: Django>=1.11,<2.0 django-20: Django>=2.0,<2.1 django-21: Django>=2.1,<2.2 django-master: https://github.com/django/django/archive/master.tar.gz postgres: psycopg2 mysql: mysqlclient [testenv:flake8] deps = Django pep257==0.7.0 pep8==1.6.2 flake8==2.5.0 flake8-docstrings==0.2.1 commands = flake8 [testenv:docs] changedir = docs deps = Sphinx dj-database-url Django commands = sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html django-nose-1.4.6/unittests/000077500000000000000000000000001335523113700157735ustar00rootroot00000000000000django-nose-1.4.6/unittests/test_databases.py000066400000000000000000000053021335523113700213330ustar00rootroot00000000000000"""Test database access without a database.""" from contextlib import contextmanager from unittest import TestCase try: from django.db.models.loading import cache as apps except: from django.apps import apps from nose.plugins.attrib import attr from django_nose.runner import NoseTestSuiteRunner class GetModelsForConnectionTests(TestCase): """Test runner._get_models_for_connection.""" tables = ['test_table%d' % i for i in range(5)] def _connection_mock(self, tables): class FakeIntrospection(object): def get_table_list(*args, **kwargs): return tables class FakeConnection(object): introspection = FakeIntrospection() def cursor(self): return None return FakeConnection() def _model_mock(self, db_table): class FakeModel(object): _meta = type('meta', (object,), {'db_table': db_table})() return FakeModel() @contextmanager def _cache_mock(self, tables=[]): def get_models(*args, **kwargs): return [self._model_mock(t) for t in tables] old = apps.get_models apps.get_models = get_models yield apps.get_models = old def setUp(self): """Initialize the runner.""" self.runner = NoseTestSuiteRunner() def test_no_models(self): """For a DB with no tables, return nothing.""" connection = self._connection_mock([]) with self._cache_mock(['table1', 'table2']): self.assertEqual( self.runner._get_models_for_connection(connection), []) def test_wrong_models(self): """If no tables exists for models, return nothing.""" connection = self._connection_mock(self.tables) with self._cache_mock(['table1', 'table2']): self.assertEqual( self.runner._get_models_for_connection(connection), []) @attr("special") def test_some_models(self): """If some of the models are in the DB, return matching models.""" connection = self._connection_mock(self.tables) with self._cache_mock(self.tables[1:3]): result_tables = [ m._meta.db_table for m in self.runner._get_models_for_connection(connection)] self.assertEqual(result_tables, self.tables[1:3]) def test_all_models(self): """If all the models have in the DB, return them all.""" connection = self._connection_mock(self.tables) with self._cache_mock(self.tables): result_tables = [ m._meta.db_table for m in self.runner._get_models_for_connection(connection)] self.assertEqual(result_tables, self.tables)