{ "info": { "author": "Thomas Welfley", "author_email": "thomas.welfley+testtube@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing", "Topic :: System :: Filesystems", "Topic :: System :: Monitoring", "Topic :: Utilities" ], "description": "Testtube\n========\n\n|Build Status| |Coverage Status| |Latest Version| |Downloads|\n\nSpare your alt and tab keys by automatically running your project's test\nsuite whenever files change.\n\n.. |Build Status| image:: https://img.shields.io/travis/thomasw/testtube.svg\n :target: https://travis-ci.org/thomasw/testtube\n.. |Coverage Status| image:: https://img.shields.io/coveralls/thomasw/testtube.svg\n :target: https://coveralls.io/r/thomasw/testtube\n.. |Latest Version| image:: https://img.shields.io/pypi/v/testtube.svg\n :target: https://pypi.python.org/pypi/testtube/\n.. |Downloads| image:: https://img.shields.io/pypi/dm/testtube.svg\n :target: https://pypi.python.org/pypi/testtube/\n\nInstallation\n------------\n\n::\n\n pip install testtube\n\ntesttube is tested with Python 2.6, 2.7, 3.2, 3.3 and 3.4, 3.5 and pypy.\n\nUsage\n-----\n\n1. Configure testtube\n~~~~~~~~~~~~~~~~~~~~~\n\nThe simplest way to configure testtube is to place a tube.py file in\nwhatever directory testtube's watch command (``stir``) will be executed in\n(this is typically a project's root directory). The tube.py file needs to define\nan iterable named ``PATTERNS`` that contains tuples which 1. specify a regular\nexpression to test the paths of changed files and 2. an iterable containing a\nlist of tests to run when a path matches the corresponding regular expression.\n\nHere's an example ``tube.py`` file:\n\n.. code:: python\n\n from testtube.helpers import Frosted, Nosetests, Pep257, Flake8\n\n PATTERNS = (\n # Run pep257 check against a file if it changes, excluding files that have\n # test_ or tube.py in the name.\n # If this test fails, don't make any noise (0 bells on failure)\n (\n r'((?!test_)(?!tube\\.py).)*\\.py$',\n [Pep257(bells=0)]\n ),\n # Run flake8 and Frosted on the entire project when a python file changes.\n # If these checks fail, abort the entire test suite because failure might\n # be due to a syntax error. There's no point running the subsequent tests\n # if there is such an error.\n (\n r'.*\\.py$',\n [Flake8(all_files=True), Frosted(all_files=True)],\n {'fail_fast': True}\n ),\n # Run the test suite whenever python or test config files change.\n (\n r'(.*setup\\.cfg$)|(.*\\.coveragerc)|(.*\\.py$)',\n [Nosetests()]\n )\n )\n\n # Don't process any file changes that match these rules\n IGNORE_PATTERNS = (\n r'.*sample/[^/]*$',\n )\n\nIn the example above, ``PATTERNS`` contains a series of patterns, coupled with a\nlist of callable tests. The second test group, which calls Flake8 and Frosted,\nincludes an optional test group configuration.\n\nAn iterable named ``IGNORE_PATTERNS`` is also specified. Any paths that match\nthese patterns will be ignored regardless of whether or not they also match a\ntest group defined in ``PATTERNS``.\n\nA test, at its simplest, is just a callable that returns ``True`` or\n``False`` after being passed the path to a changed file and a regular\nexpression match object for the path's match against the corresponding test\ngroup's regular expression. The example uses several helpers that ship with\ntesttube. These helpers are callable objects that can be configured in\nvarious ways when they are instantiated.\n\nTesttube comes with a number of such helpers, which can be found in\n`helpers.py `_.\nThey are designed to save consumers from specifying their own tests as much as\nis possible. If they are insufficient for a specific project, please see\n`Writing custom tests`_.\n\nIncluded helpers:\n\n- Pep8\n- Pyflakes\n- Frosted\n- Pep257\n- Nosetests\n- PythonSetupPyTest (runs python setup.py when matching files change)\n- ClearScreen (clears the screen)\n\nHelpers typically accept the following arguments when instantiated:\n\n- ``all_files``: run the test against the entire source directory\n instead of just the changed file (which is the default behavior)\n- ``fail_fast``: Abort running the rest of the test group if the test\n fails.\n- ``bells``: On failure, testtube will audibly notify the user 3 times\n unless otherwise specified\n- ``name``: The name of the test in test report output\n\nThe following generates a pep8 test configured to run against all files,\nabort processing of its test group on failure, alert the user 5 times\naudibly, and show up as \"follow pep8 dude\" in test report output:\n\n.. code:: python\n\n from testtube.helpers import Pep8\n\n helper = Pep8(\n all_files=True, fail_fast=True, bells=5, name='follow pep8 dude')\n\nNote that helpers, once instantiated, are just callables that return\n``True`` or ``False``:\n\n.. code:: python\n\n # Once configured, helpers are callables (they act like methods) that\n # accept a path to a python file and a regex match object (though the\n # match object isn't a requirement).\n\n helper('/path/to/some/file.py', None)\n\nAnd here's that same example fully incorporated into a tube.py file:\n\n.. code:: python\n\n from testtube.helpers import Pep8\n\n\n PATTERNS = [\n [\n # Pattern\n r'.*\\.py$',\n # list of callable tests to run\n [\n Pep8(\n all_files=True, fail_fast=True, bells=5,\n name='follow pep8 dude')\n ]\n ]\n ]\n\nThe behavior of helpers can be customized as necessary by overriding\nspecific methods. See\n`helpers.py `_\nfor further information.\n\nIn addition to configuring helpers, test groups can also be configured:\n\n- ``fail_fast``: abort processing of subsequent test groups if all\n tests in the configured group did not pass.\n\nIn the first example tube.py file, the second test group is configured\nto abort the rest of the test suite if either ``Flake8`` or ``Frosted``\nfail.\n\n2. Stir it\n~~~~~~~~~~\n\nOnce a tube.py file is in place, tell testtube to watch the project for\nchanges:\n\n::\n\n $ stir\n testtube is now watching /Path/to/CWD/ for changes...\n\nBy default, stir will watch the current working directory and configure\nitself with a settings module named ``tube`` (tube.py). If the tube.py file was\nplaced in the project root directory, then one shouldn't need to specify\nany parameters assuming stir is executed from that same directory. If paths need\nto be customized a bit, ``stir -h`` will light the way:\n\n::\n\n $ stir -h\n usage: stir [-h] [--src_dir SRC_DIR] [--settings SETTINGS]\n\n Watch a directory and run a custom set of tests whenever a file changes.\n\n optional arguments:\n -h, --help show this help message and exit\n --src_dir SRC_DIR The directory to watch for changes. (Defaults to CWD)\n --settings SETTINGS Path to a testtube settings file that defines which\n tests to run (Defaults to \"tube.py\" - your settings\n file must be importable and the path must be relative\n to your CWD)\n\nWriting custom tests\n--------------------\n\nIf the included helpers don't meet the specific needs of a project, custom tests\ncan be defined directly in tube.py. Simply define a callable that accepts two\narguments and add it to the ``PATTERNS`` list:\n\n.. code:: python\n\n def mytest(changed_file, match_obj):\n print \"Oh snap, %s just changed\" % changed_file\n\n PATTERNS = (\n (r'.*', [mytest]),\n )\n\nIf a custom test needs to be configurable like the builtin helpers or if it\nneeds to make system calls, extending the base helper class\n(``testtube.helpers.Helper``) and customizing the behavior as is necessary is\nusually the simplest approach. The following is a tube.py file which defines a\nconfigureable test that outputs the file tree for the entire project each time a\npython file changes:\n\n.. code:: python\n\n from testtube.helpers import Helper\n\n\n class ProjectTree(Helper):\n # The built in helper class is designed to make writing tests that make\n # system calls easy. Overriding `command` is all that's usually\n # necessary\n command = 'tree'\n all_files = True\n\n def __init__(self, **kwargs):\n # TreeOutput only works on all files, so override any contrary conf\n kwargs['all_files'] = True\n\n super(ProjectTree, self).__init__(kwargs)\n\n PATTERNS = (\n (r'.*\\.py$', [ProjectTree(bells=1)]),\n )\n\nNote that this example requires tree to be installed on the system\n(``$ brew install tree`` for OS X users).\n\nCaveats\n-------\n\n- The distinction between ``r'.*\\.py'`` and ``r'.*\\.py$'`` is significant.\n Without the trailing ``$``, testtube will run tests every time pyc\n files change. That's very likely to not be useful.\n- testtube doesn't currently reload its own configuration when it\n changes. If tube.py is modified, testtube will need to be restarted.\n\nLocal development\n-----------------\n\nInstall the development requirements using the included requirements.txt file:\n\n::\n\n pip install -r requirements.txt\n\nIt is often useful to use to use the checkout of testtube that's currently under\ndevelopment to monitor itself using its included tube.py file. Use testtube to\nhelp build testtube. This can be achieved by installing the checkout as an\neditable. Execute the following from the project root and then use the ``stir``\ncommand as one usually would:\n\n::\n\n pip install -e ./\n\nNote that testtube will need to be restarted for code changes to take effect.\n\nEverything else\n---------------\n\nCopyright (c) `Thomas Welfley `_. See\n`LICENSE `_\nfor details.\n\n\nChangelog\n=========\n\n1.1.0\n-----\n\n- Simplify dev environment configuration.\n- Add tox configuration for locally testing against multiple python versions.\n- Fix a bug in the nosetests helper implementation that was making it\n ignore passed in configuration.\n- Fix a bug that causing testtube to choke on simple method based tests.\n- Add an ``IGNORE_PATTERNS`` configuration option which supersedes test group\n pattern matches and allows users to configure testtube to always ignore\n certain files.\n- Add integration tests.\n- Factor out threading anti-patterns from core (``time.sleep()``).\n\n1.0.0\n-----\n\n- Make tests configurable\n- Make test groups configurable\n- Centralizes output in a renderer object\n- Adds support for audible bells\n- Adds test group fail fast support (aborts test run)\n- Adds test fail fast support (aborts test group)\n- Adds helper base class to make writing tests easier\n- Adds a frosted helper\n- Rewrite of configuration handling\n- Eliminates redundant helpers: pep8\\_all, pyflakes\\_all,\n nosetests\\_all\n\n0.2.0\n-----\n\n- Added python 3 support\n\n0.0.1\n-----\n\n- Initial release", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/thomasw/testtube", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "testtube", "package_url": "https://pypi.org/project/testtube/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/testtube/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/thomasw/testtube" }, "release_url": "https://pypi.org/project/testtube/1.1.0/", "requires_dist": null, "requires_python": null, "summary": "testtube is a Python based continuous test runner.\n\ntesttube's stir command can, based on a tube.py configuration file, monitor\na directory for file changes and execute a pre-configured set of tests when\nchanges occur.", "version": "1.1.0" }, "last_serial": 2165345, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "4faa62e496663d67a486a631005c81fe", "sha256": "d9601f3760fa5c694ae144fca444300226715ac64d2ea6146ece18685532cda2" }, "downloads": -1, "filename": "testtube-0.0.1.tar.gz", "has_sig": false, "md5_digest": "4faa62e496663d67a486a631005c81fe", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5624, "upload_time": "2012-12-16T22:58:02", "url": "https://files.pythonhosted.org/packages/7f/40/04cd4f9ee2a6f204038466225dc858777799fbd7ebd574da92c744f1ecba/testtube-0.0.1.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "c13fb4052668c2a0e6df9f8824360a50", "sha256": "618aa8e4a51f7f1fba37edfaa3800a16fceca235c462195b3349621178fe9490" }, "downloads": -1, "filename": "testtube-0.2.0.tar.gz", "has_sig": false, "md5_digest": "c13fb4052668c2a0e6df9f8824360a50", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6837, "upload_time": "2014-04-06T04:32:32", "url": "https://files.pythonhosted.org/packages/a7/b9/76cf387eff599483acf71f01d78969e26a02c63392c2d6df6e3b176695b4/testtube-0.2.0.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "84ad4d9cf17c296141f07c28a4f7d8e6", "sha256": "072e3f7f2b495cd195dd930355c62fafba27ea1eb1fc16725872d17186bfd8df" }, "downloads": -1, "filename": "testtube-1.0.0.tar.gz", "has_sig": false, "md5_digest": "84ad4d9cf17c296141f07c28a4f7d8e6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11952, "upload_time": "2014-10-06T08:15:30", "url": "https://files.pythonhosted.org/packages/56/5f/cc78b51559da51567f271699fef9561c54daae217502e93bb28811488529/testtube-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "cabe1861cb0e9f996fe0ead41166a120", "sha256": "5969786d1260462466b4bd690ef808dba4d37ab94791d232353696972beaf4ec" }, "downloads": -1, "filename": "testtube-1.1.0-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "cabe1861cb0e9f996fe0ead41166a120", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 24768, "upload_time": "2016-06-13T20:39:58", "url": "https://files.pythonhosted.org/packages/bc/47/569ca3e1dbe8dd133c7dba273bc15251dcc94a983e61f6989a3fb6888011/testtube-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b797f031c1f233eef5a733077e12e72f", "sha256": "f891abe5d365add658df8140b3ab58cf66bc991f56a62e791d42ad3a4c2643a4" }, "downloads": -1, "filename": "testtube-1.1.0.tar.gz", "has_sig": true, "md5_digest": "b797f031c1f233eef5a733077e12e72f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20777, "upload_time": "2016-06-13T20:42:20", "url": "https://files.pythonhosted.org/packages/9e/e7/4f3e2da8e7e1129749150b77558f2bc17d7264af6ba5aaad9bf2841c0a80/testtube-1.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "cabe1861cb0e9f996fe0ead41166a120", "sha256": "5969786d1260462466b4bd690ef808dba4d37ab94791d232353696972beaf4ec" }, "downloads": -1, "filename": "testtube-1.1.0-py2.py3-none-any.whl", "has_sig": true, "md5_digest": "cabe1861cb0e9f996fe0ead41166a120", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 24768, "upload_time": "2016-06-13T20:39:58", "url": "https://files.pythonhosted.org/packages/bc/47/569ca3e1dbe8dd133c7dba273bc15251dcc94a983e61f6989a3fb6888011/testtube-1.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b797f031c1f233eef5a733077e12e72f", "sha256": "f891abe5d365add658df8140b3ab58cf66bc991f56a62e791d42ad3a4c2643a4" }, "downloads": -1, "filename": "testtube-1.1.0.tar.gz", "has_sig": true, "md5_digest": "b797f031c1f233eef5a733077e12e72f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20777, "upload_time": "2016-06-13T20:42:20", "url": "https://files.pythonhosted.org/packages/9e/e7/4f3e2da8e7e1129749150b77558f2bc17d7264af6ba5aaad9bf2841c0a80/testtube-1.1.0.tar.gz" } ] }