tap.py-3.0/0000755000076500000240000000000013606112130012650 5ustar mattstaff00000000000000tap.py-3.0/AUTHORS0000644000076500000240000000047113606111604013727 0ustar mattstaff00000000000000tappy was originally created by Matt Layman. Contributors ------------ * Andrew McNamara * Chris Clarke * Erik Cederstrand * Marc Abramowitz * Mark E. Hamilton * Matt Layman * meejah (https://meejah.ca) * Michael F. Lamb (http://datagrok.org) * Nicolas Caniart * Richard Bosworth * Ross Burton * Simon McVittie tap.py-3.0/LICENSE0000644000076500000240000000247613540027652013701 0ustar mattstaff00000000000000Copyright (c) 2019, Matt Layman and contributors. See AUTHORS for more details. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 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 HOLDER 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. tap.py-3.0/MANIFEST.in0000644000076500000240000000017413575305555014434 0ustar mattstaff00000000000000include AUTHORS include LICENSE include README.md recursive-include docs * recursive-include tap/locale * prune docs/_build tap.py-3.0/PKG-INFO0000644000076500000240000002035013606112130013745 0ustar mattstaff00000000000000Metadata-Version: 2.1 Name: tap.py Version: 3.0 Summary: Test Anything Protocol (TAP) tools Home-page: https://github.com/python-tap/tappy Author: Matt Layman Author-email: matthewlayman@gmail.com License: BSD Description: tappy is a set of tools for working with the `Test Anything Protocol (TAP) `_, a line based test protocol for recording test data in a standard way. Follow tappy development on `GitHub `_. Developer documentation is on `Read the Docs `_. Releases ======== Version 3.0, Released January 10, 2020 -------------------------------------- * Drop support for Python 2 (it is end-of-life). * Add support for subtests. * Run a test suite with ``python -m tap``. * Discontinue use of Pipenv for managing development. Version 2.6.2, Released October 20, 2019 ---------------------------------------- * Fix bug in streaming mode that would generate tap files when the plan was already set (affected pytest). Version 2.6.1, Released September 17, 2019 ------------------------------------------ * Fix TAP version 13 support from more-itertools behavior change. Version 2.6, Released September 16, 2019 ---------------------------------------- * Add support for Python 3.7. * Drop support for Python 3.4 (it is end-of-life). Version 2.5, Released September 15, 2018 ---------------------------------------- * Add ``set_plan`` to ``Tracker`` which allows producing the ``1..N`` plan line before any tests. * Switch code style to use Black formatting. Version 2.4, Released May 29, 2018 ---------------------------------- * Add support for producing TAP version 13 output to streaming and file reports by including the ``TAP version 13`` line. Version 2.3, Released May 15, 2018 ---------------------------------- * Add optional method to install tappy for YAML support with ``pip install tap.py[yaml]``. * Make tappy version 13 compliant by adding support for parsing YAML blocks. * ``unittest.expectedFailure`` now uses a TODO directive to better align with the specification. Version 2.2, Released January 7, 2018 ------------------------------------- * Add support for Python 3.6. * Drop support for Python 3.3 (it is end-of-life). * Use Pipenv for managing development. * Switch to pytest as the development test runner. Version 2.1, Released September 23, 2016 ---------------------------------------- * Add ``Parser.parse_text`` to parse TAP provided as a string. Version 2.0, Released July 31, 2016 ----------------------------------- * Remove nose plugin. The plugin moved to the ``nose-tap`` distribution. * Remove pytest plugin. The plugin moved to the ``pytest-tap`` distribution. * Remove Pygments syntax highlighting plugin. The plugin was merged upstream directly into the Pygments project and is available without tappy. * Drop support for Python 2.6. Version 1.9, Released March 28, 2016 ------------------------------------ * ``TAPTestRunner`` has a ``set_header`` method to enable or disable test case header ouput in the TAP stream. * Add support for Python 3.5. * Perform continuous integration testing on OS X. * Drop support for Python 3.2. Version 1.8, Released November 30, 2015 --------------------------------------- * The ``tappy`` TAP consumer can read a TAP stream directly from STDIN. * Tracebacks are included as diagnostic output for failures and errors. * The ``tappy`` TAP consumer has an alternative, shorter name of ``tap``. * The pytest plugin now defaults to no output unless provided a flag. Users dependent on the old default behavior can use ``--tap-files`` to achieve the same results. * Translated into Arabic. * Translated into Chinese. * Translated into Japanese. * Translated into Russian. * Perform continuous integration testing on Windows with AppVeyor. * Improve unit test coverage to 100%. Version 1.7, Released August 19, 2015 ------------------------------------- * Provide a plugin to integrate with pytest. * Document some viable alternatives to tappy. * Translated into German. * Translated into Portuguese. Version 1.6, Released June 18, 2015 ----------------------------------- * ``TAPTestRunner`` has a ``set_stream`` method to stream all TAP output directly to an output stream instead of a file. results in a single output file. * The ``nosetests`` plugin has an optional ``--tap-stream`` flag to stream all TAP output directly to an output stream instead of a file. * tappy is now internationalized. It is translated into Dutch, French, Italian, and Spanish. * tappy is available as a Python wheel package, the new Python packaging standard. Version 1.5, Released May 18, 2015 ---------------------------------- * ``TAPTestRunner`` has a ``set_combined`` method to collect all results in a single output file. * The ``nosetests`` plugin has an optional ``--tap-combined`` flag to collect all results in a single output file. * ``TAPTestRunner`` has a ``set_format`` method to specify line format. * The ``nosetests`` plugin has an optional ``--tap-format`` flag to specify line format. Version 1.4, Released April 4, 2015 ----------------------------------- * Update ``setup.py`` to support Debian packaging. Include man page. Version 1.3, Released January 9, 2015 ------------------------------------- * The ``tappy`` command line tool is available as a TAP consumer. * The ``Parser`` and ``Loader`` are available as APIs for programmatic handling of TAP files and data. Version 1.2, Released December 21, 2014 --------------------------------------- * Provide a syntax highlighter for Pygments so any project using Pygments (e.g., Sphinx) can highlight TAP output. Version 1.1, Released October 23, 2014 -------------------------------------- * ``TAPTestRunner`` has a ``set_outdir`` method to specify where to store ``.tap`` files. * The ``nosetests`` plugin has an optional ``--tap-outdir`` flag to specify where to store ``.tap`` files. * tappy has backported support for Python 2.6. * tappy has support for Python 3.2, 3.3, and 3.4. * tappy has support for PyPy. Version 1.0, Released March 16, 2014 ------------------------------------ * Initial release of tappy * ``TAPTestRunner`` - A test runner for ``unittest`` modules that generates TAP files. * Provides a plugin for integrating with **nose**. Keywords: TAP,unittest Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Testing Provides-Extra: yaml tap.py-3.0/README.md0000644000076500000240000001030013573576145014150 0ustar mattstaff00000000000000tappy ===== [![PyPI version][pypishield]](https://pypi.python.org/pypi/tap.py) [![BSD license][license]](https://raw.githubusercontent.com/python-tap/tappy/master/LICENSE) [![Linux status][travis]](https://travis-ci.org/python-tap/tappy) [![OS X status][travisosx]](https://travis-ci.org/python-tap/tappy) [![Windows status][appveyor]](https://ci.appveyor.com/project/mblayman/tappy) [![Coverage][coverage]](https://codecov.io/github/python-tap/tappy) TAP logo tappy is a set of tools for working with the [Test Anything Protocol (TAP)][tap] in Python. TAP is a line based test protocol for recording test data in a standard way. Full documentation for tappy is at [Read the Docs][rtd]. The information below provides a synopsis of what tappy supplies. For the curious: tappy sounds like "happy." If you find tappy useful, please consider starring the repository to show a kindness and help others discover something valuable. Thanks! Installation ------------ tappy is available for download from [PyPI][pypi]. tappy is currently supported on Python 3.5, 3.6, 3.7, and PyPy. It is continuously tested on Linux, OS X, and Windows. ```bash $ pip install tap.py ``` For testing with [pytest][pytest], you only need to install `pytest-tap`. ```bash $ pip install pytest-tap ``` For testing with [nose][ns], you only need to install `nose-tap`. ```bash $ pip install nose-tap ``` TAP version 13 brings support for [YAML blocks](http://testanything.org/tap-version-13-specification.html#yaml-blocks) associated with test results. To work with version 13, install the optional dependencies. Learn more about YAML support in the [TAP version 13](http://tappy.readthedocs.io/en/latest/consumers.html#tap-version-13) section. ```bash $ pip install tap.py[yaml] ``` Motivation ---------- Some projects have mixed programming environments with many programming languages and tools. Because of TAP's simplicity, it can function as a *lingua franca* for testing. When every testing tool can create TAP, a team can get a holistic view of their system. Python did not have a bridge from `unittest` to TAP so it was difficult to integrate a Python test suite into a larger TAP ecosystem. tappy is Python's bridge to TAP. ![TAP streaming demo][stream] Goals ----- 1. Provide [TAP Producers][produce] which translate Python's `unittest` into TAP. 2. Provide a [TAP Consumer][consume] which reads TAP and provides a programmatic API in Python or generates summary results. 3. Provide a command line interface for reading TAP. Producers --------- * `TAPTestRunner` - This subclass of `unittest.TextTestRunner` provides all the functionality of `TextTestRunner` and generates TAP files. * tappy for [nose][ns] - `nose-tap` provides a plugin for the **nose** testing tool. * tappy for [pytest][pytest] - `pytest-tap` provides a plugin for the **pytest** testing tool. Consumers --------- * `tappy` - A command line tool for processing TAP files. * `Loader` and `Parser` - Python APIs for handling of TAP files and data. Contributing ------------ The project welcomes contributions of all kinds. Check out the [contributing guidelines][contributing] for tips on how to get started. [tap]: http://testanything.org/ [pypishield]: https://img.shields.io/pypi/v/tap.py.svg [license]: https://img.shields.io/pypi/l/tap.py.svg [shield]: https://img.shields.io/pypi/dm/tap.py.svg [travis]: https://img.shields.io/travis/python-tap/tappy/master.svg?label=linux+build [travisosx]: https://img.shields.io/travis/python-tap/tappy/master.svg?label=os+x++build [appveyor]: https://img.shields.io/appveyor/ci/mblayman/tappy/master.svg?label=windows+build [coverage]: https://img.shields.io/codecov/c/github/python-tap/tappy.svg [rtd]: http://tappy.readthedocs.io/en/latest/ [pypi]: https://pypi.python.org/pypi/tap.py [stream]: https://github.com/python-tap/tappy/blob/master/docs/images/stream.gif [produce]: http://testanything.org/producers.html [consume]: http://testanything.org/consumers.html [ns]: https://nose.readthedocs.io/en/latest/ [pytest]: http://pytest.org/latest/ [contributing]: http://tappy.readthedocs.io/en/latest/contributing.html tap.py-3.0/docs/0000755000076500000240000000000013606112130013600 5ustar mattstaff00000000000000tap.py-3.0/docs/Makefile0000644000076500000240000001514612514255661015265 0ustar mattstaff00000000000000# 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) -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/tappy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/tappy.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/tappy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/tappy" @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." tap.py-3.0/docs/_static/0000755000076500000240000000000013606112130015226 5ustar mattstaff00000000000000tap.py-3.0/docs/_static/.keep0000644000076500000240000000000012514255661016157 0ustar mattstaff00000000000000tap.py-3.0/docs/alternatives.rst0000644000076500000240000000333512654531741017056 0ustar mattstaff00000000000000Alternatives ============ tappy is not the only project that can produce TAP output for Python. While tappy is a capable TAP producer and consumer, other projects might be a better fit for you. The following comparison lists some other Python TAP tools and lists some of the biggest differences compared to tappy. pycotap ------- pycotap is a good tool for when you want TAP output, but you don't want extra dependencies. pycotap is a zero dependency TAP producer. It is so small that you could even embed it into your project. `Check out the project homepage `_. catapult -------- catapult is a TAP producer. catapult is also capable of producing TAP-Y and TAP-J which are YAML and JSON test streams that are inspired by TAP. `You can find the catapult source on GitHub `_. pytap13 ------- pytap13 is a TAP consumer for TAP version 13. It parses a TAP stream and produces test instances that can be inspected. `pytap13's homepage is on Bitbucket `_. bayeux ------ bayeux is a TAP producer that is designed to work with unittest and unittest2. `bayeux is on GitLab. `_. taptaptap --------- taptaptap is a TAP producer with a procedural style similar to Perl. It also includes a ``TapWriter`` class as a TAP producer. `Visit the taptaptap homepage `_. unittest-tap-reporting ---------------------- unittest-tap-reporting is another zero dependency TAP producer. `Check it out on GitHub `_. If there are other relevant projects, please post an issue on GitHub so this comparison page can be updated accordingly. tap.py-3.0/docs/conf.py0000644000076500000240000002020113540027652015105 0ustar mattstaff00000000000000# -*- coding: utf-8 -*- # # tappy documentation build configuration file, created by # sphinx-quickstart on Tue Mar 11 20:21:22 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. import sys import os sys.path.append(os.path.abspath('..')) from tap import __version__ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- 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', ] # autodoc settings autodoc_member_order = 'bysource' # 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 = u'tappy' copyright = u'2019, Matt Layman and contributors' # 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 = 'default' # 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 = 'tappydoc' # -- 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', 'tappy.tex', u'tappy Documentation', u'Matt Layman and contributors', '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 = [ ('tappy.1', 'tappy', u'a tap consumer for python', [], 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', 'tappy', u'tappy Documentation', u'Matt Layman and contributors', 'tappy', 'One line description of project.', 'Miscellaneous'), ] # 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 tap.py-3.0/docs/consumers.rst0000644000076500000240000001112613276557472016402 0ustar mattstaff00000000000000TAP Consumers ============= tappy Tool ---------- The ``tappy`` command line tool is a `TAP consumer `_. The tool accepts TAP files or directories containing TAP files and provides a standard Python ``unittest`` style summary report. Check out ``tappy -h`` for the complete list of options. You can also use the tool's shorter alias of ``tap``. .. code-block:: console $ tappy *.tap ................F.................................. ====================================================================== FAIL: - The parser extracts a bail out line. ---------------------------------------------------------------------- ---------------------------------------------------------------------- Ran 51 tests in 0.002s FAILED (failures=1) TAP Stream ~~~~~~~~~~ ``tappy`` can read a TAP stream directly STDIN. This permits any TAP producer to pipe its results to ``tappy`` without generating intermediate output files. ``tappy`` will read from STDIN when no arguments are provided or when a dash character is the only argument. Here is an example of ``nosetests`` piping to ``tappy``: .. code-block:: console $ nosetests --with-tap --tap-stream 2>&1 | tappy ...................................................................... ............................................... ---------------------------------------------------------------------- Ran 117 tests in 0.003s OK In this example, ``nosetests`` puts the TAP stream on STDERR so it must be redirected to STDOUT because the Unix pipe expects input on STDOUT. ``tappy`` can use redirected input from a shell. .. code-block:: console $ tappy < TestAdapter.tap ........ ---------------------------------------------------------------------- Ran 8 tests in 0.000s OK This final example shows ``tappy`` consuming TAP from Perl's test tool, ``prove``. The example includes the optional dash character. .. code-block:: console $ prove t/array.t -v | tappy - ............ ---------------------------------------------------------------------- Ran 12 tests in 0.001s OK API --- In addition to a command line interface, tappy enables programmatic access to TAP files for users to create their own TAP consumers. This access comes in two forms: 1. A ``Loader`` class which provides a ``load`` method to load a set of TAP files into a ``unittest.TestSuite``. The ``Loader`` can receive files or directories. .. code-block:: pycon >>> loader = Loader() >>> suite = loader.load(['foo.tap', 'bar.tap', 'baz.tap']) 2. A ``Parser`` class to provide a lower level interface. The ``Parser`` can parse a file via ``parse_file`` and return parsed lines that categorize the file contents. .. code-block:: pycon >>> parser = Parser() >>> for line in parser.parse_file('foo.tap'): ... # Do whatever you want with the processed line. ... pass The API specifics are listed below. .. autoclass:: tap.loader.Loader :members: .. autoclass:: tap.parser.Parser :members: .. _tap-version-13: TAP version 13 ~~~~~~~~~~~~~~ The specification for TAP version 13 adds support for `yaml blocks `_ to provide additional information about the preceding test. In order to consume yaml blocks, ``tappy`` requires `pyyaml `_ and `more-itertools `_ to be installed. These dependencies are optional. If they are not installed, TAP output will still be consumed, but any yaml blocks will be parsed as :class:`tap.line.Unknown`. If a :class:`tap.line.Result` object has an associated yaml block, :attr:`~tap.line.Result.yaml_block` will return the block converted to a ``dict``. Otherwise, it will return ``None``. ``tappy`` provides a strict interpretation of the specification. A yaml block will only be associated with a result if it immediately follows that result. Any :class:`diagnostic ` between a :class:`result ` and a yaml block will result in the block lines being parsed as :class:`tap.line.Unknown`. Line Categories ~~~~~~~~~~~~~~~ The parser returns ``Line`` instances. Each line contains different properties depending on its category. .. autoclass:: tap.line.Result :members: .. autoclass:: tap.line.Plan :members: .. autoclass:: tap.line.Diagnostic :members: .. autoclass:: tap.line.Bail :members: .. autoclass:: tap.line.Version :members: .. autoclass:: tap.line.Unknown :members: tap.py-3.0/docs/contributing.rst0000644000076500000240000000230713575305555017067 0ustar mattstaff00000000000000Contributing ============ tappy should be easy to contribute to. If anything is unclear about how to contribute, please submit an issue on GitHub so that we can fix it! How ----- Fork tappy on `GitHub `_ and `submit a Pull Request `_ when you're ready. Setup ----- tappy uses the built-in `venv` module. .. code-block:: console $ git clone git@github.com:python-tap/tappy.git $ cd tappy $ python3 -m venv venv $ source venv/bin/activate $ pip install -r requirements-dev.txt $ # Edit some files and run the tests. $ pytest The commands above show how to get a tappy clone configured. If you've executed those commands and the test suite passes, you should be ready to develop. Guidelines ---------- 1. Code uses Black style. Please run it through ``black tap`` to autoformat. 2. Make sure your change works against master! (Bonus points for unit tests.) 3. Document your change in the ``docs/releases.rst`` file. 4. For first time contributors, please add your name to ``AUTHORS`` so you get attribution for you effort. This is also to recognize your claim to the copyright in the project. tap.py-3.0/docs/highlighter.rst0000644000076500000240000000115712747173460016656 0ustar mattstaff00000000000000TAP Syntax Highlighter for Pygments =================================== `Pygments `_ contains an extension for syntax highlighting of TAP files. Any project that uses Pygments, like `Sphinx `_, can take advantage of this feature. This highlighter was initially implemented in tappy. Since the highlighter was merged into the upstream Pygments project, tappy is no longer a requirement to get TAP syntax highlighting. Below is an example usage for Sphinx. .. code-block:: rst .. code-block:: tap 1..2 ok 1 - A passing test. not ok 2 - A failing test. tap.py-3.0/docs/images/0000755000076500000240000000000013606112130015045 5ustar mattstaff00000000000000tap.py-3.0/docs/images/python-tap.png0000644000076500000240000006366412747173460017717 0ustar mattstaff00000000000000PNG  IHDR瘬9&s\\Lفq'<ǭ>`d] Pu} m\c3d/0-Cx^d?G!ԯ{2?캑R.~$hp<( ,cH4J"`0 Iwl2.}0vNx≫0b2۟.(JF(\ O@xM}8 dIf?yHƊ$ff3 Q`GgM#?W^aϞ=LjX,6lج,UWrܹ|t0BmE"9ºu[TO?E ~O|[̛;&{99Պi֯(rͬ#GD"2}:3f孷7J4 ׿uXd ---=Pc,]k׮e{エ$ZP~ /~#<_g޼y=FjjjXhр-[0ͬ_3j*$pk]΋/w;v\A.S[[… qw}~^txGG]]].ޡ:̾tvDX,6(@8w}TWW˗ 6RVV[o 6`Z~ f\z^z^Iy1۷rJb7nebR)I\oUE,JX,U~dÆ \r?KiuQ]]ݯWty1(--geÆ X,l2GT/rwiӦA5H_b/BAt]ąM&>ps>M < I$!gefmƒ%K8$s6mĪUz4׾ڗ鏤_,Xaݺudee>WloA0`4z;2ko6xmdD"A41%gΜ1%?я}c֭,X85h# &L EJ(;@u..V^{\AxGy <3=￟:;;y'dff^7<hEQ|X,syyy ҦȨ=kjjѣ466b4{Cz+JYY,YV~\@X,l޼KꪫU#\9\zj.֬Yy=yuW$ER/,Zwyt:{ .PPPpɈIEiwLnn.>oflق((B<!דoYYyyl|I^~/Ņ&33s撖(aύ7 { Rv/0^z%z n]ׇğ!_l6sqA`ժU=gbRn^cݺuTVVyfER)sx ü=d2p^&d2R!{n?Oz4w޽{{Ջ}i[- seN+%zym̱cQqw\\A/^fd2qI^/-E>p}Kmd⣏>BUU.\޽{ٻw/K,a֭1nܸ5Ƌ/HUUUqL1 ;vGvi?CdժUy}=رKGns)m6`ޤ/vqn0X,>z^DQH$|cܸq޽]/yV_|ddić~أ}F#ϿDc\ TBS0"Z997s I^ TBs3sLVZEEE[nE!sĉKL@u]l6SWWwsz Aa2.9.I3bMx!@nV~?F*WrW2NT>rϐѰ:t. [n_oɓl۶mHЎ6oc=l۶,˽F`Ǐ_2S3/رc>|O"?R21 . &ohTB1 `kG+Wp\A.F"֭[W\AEEŰq/Z(5b44痊bXKqv^ /F>CA?)?0N7ė,Y2\SV_y_8zjDQd޽u ۔|!sUU4 ='JCS/7X,F8 D"4M뵬,) : .رc<Fl’%Kشiӈy?8i!␃9) WpժU5sri^{>p1Լiv (.. 1 NCCttt{&!}?Ȳ'|,̟?R-[F4:dP\]ܿY,D|CV)p477( dڵ:848#_eVXqb^H$p8@SOWMT'r\L N"F<,[Pl%AMīiPLOBRk&_TSSS1rs:,fkbAYLMM z/S)9哽68|؏O~5,t @zzzhJSMxW?>/TUeƌ̟?B$_3rr,D)3Pt56B"Bvh$D}C'IБ%Ԍt  V>Y.3#4?=ʧ&?Y8n孷ޢ}EQ7ɞ={XjU+ 5yyYPB:P%xES[O=ԐȒ$Ix^qdYFKF^j``= &?xq2o0cdL.@x1#5(FgDc1Nhigtl;%C3ۤ3gP-:h;kq?ٳJrLF6?3^'`Ҥ)qڵ}!I%f5LzOzzYF* ;uTTm۠` Eé{kx_ϸX2tr뭷R\\L"?>Ͻk]CQc BsGB x Œ8:s2Z쯑Px)Kny9ɠdPw ;Yxf38 `{)+ZCM~9?`otRٹs'2w0-0R$#NUmM;uDLrss9w5k.ݯ?̬Ѯbžhta"$D ( MBk)*P$4y6 7Նh6rvG''i(Xv irLgԨē#<̞=ƶmP_0TYT M0LŚ5kO?WWt?u5xJAM̸[[1!M%cC4T]BxTAImfdv~p"o@AΠF'U&4A$+Dž`QMm ӿ߶m&N(lٲe@t:Yz5.nu5_y7/ _CRi SkFMb /M q2HtH$b$1 ӎP"w0c  E %5d&!"]%/݌esя?K ]$K 3f g?[n͛79jV+WĊV1oY9t!,ix;cD$Lx'(!A#(HQe#`L^](.p4zqX3cd$ktJ+c%#P(+/8A';(+.`Zq.M̎eT{/e׻8q"˖-7f)z 7PTTDmM57|W\{]QFv jD#LZ<>*[ttD1IAB b L> ÜEQi>6=@,B  O_-DfF6FQ HBASdPHab ,2]&ZcS}|;~oYfxسgcdܹL:P(-xM< gHapMi hl3IWT􄎠 4 :j8A[{'N2 t(+I"!F‘$=8hD":QUnjD* s&&.`M|ӟg444,&/ UUdfs̽*p ;'k mK~RαJ{[u^dLbL/3ŕW  de;2./ {H34 t`G %T85u]y4'e3*f `b\Y>]Ab8$芄̰ǑncۇDC+%/FRM92(+!^;q=/ ]-WB-06L RDȂ%>]D"aT, 390 nQţ%ZtAj6SD: 4 Fs9j0K:ݾ.͙mX iN;ͭnv;.VB1 _8٨sw_^*.kAӍ`Lzh:ݧC;TZ`(ԎT)0E[g4,999Þϑj|VV֐窪ά϶mO~f,gtDNV'ES!kT&mz%e4{h$== & ѧ޵Yq7A&,jj.NCG Wf6Yaf+9WFjf.'33?IɢJcd IDAT?z0m}0 XI,'f[;yWXudff9|>\_NRK g;y̩S3u#VMC)DQ˩EH[e@Q]ϸFB123w?Ƶڎ28A;dD ju-L4M ;/SP}*Ս͞/lhi!9\,?fō+Wի[pe*/ zC#c pl7:|Ú)>b|??*D݌IQRA҉%%bmnt]E0\ jj:9ӌAEwv!T99̘^¸Ă!E0tfO/{sҘ墮,txpɜNl3>9JbJnf-gY4e%:YtX}z{_1(1^|5}VjM {tɁn0p*3 +?AG1. Ma$ M%QƯ5:/橧bC|20OUZZʔ)SAJM]z_躯Ubc49s&$56kWqL(tvt8}1ndNl _NVA.7pn?$f2ϴYӐ ISKin{7lr>ȒL">޽f7{vp :b("i:ׁf PhiqwSXd & 98uj'Ų˘9s&v8+~ J9{*\&MΦX,ƫ[[2w9FGE & ;?}_#RUq#Oc[v"L:Qٜ=SKkS#4 QU`Ta>~gO\Ë Li0#xC"Ӓa%b0LZֿ=g!$Iޡ-JW&cD%&P*p])(+@Ls<Ƿ0Ip '*4dӧ@(wil6FGLy=~6\N ,A{k'^\Ɩ#[_ebb+YstuvP<6͎ @1qf\5,\a%%8Sust`kimk ]g8Q,\3$kǙn% "2 clr[?9HKc0RFg$$[oz3$P(t C@l8Y&𕕕=WVV^4ӱl˔鳨;IGGқD{GLaa.9ٙԞ:,dk:)SF;] Ph&,5GzW.X('O<'m覦QAKt{<̘F]ORh rYt>^f/&c2A5FyyGO5:6 ]LG#`taPU-#qߤA+{'K3a/>_<!<| 4UFtFz1&$$GRZt ,ɻnm&Ù@WKUGPT\H{-9?$P]-n7oahLV+Wp\NBᤂꨚ+D<*H"4 D,mV| I)))!--~5peW1 FZԖ޿ [FX_ڧرȶ<" d2dȡ.[½o"Kr1=r>`_ަ @OryHhtJ81UTR{u5 }QLy6:"xڛllb#X29QEyyݒ衇zӉ㨉&I5`%% Q߰Q~'i HЄudfhmjqt= X~.ltƨ45GUtA@UPu'1[,&:b4b(spiy))&W>?qYMqZ 8Xj"j&;7__(!26w,IsXzױ&# D~}]U%!d͚5?3~ƌjݎ `2[H HNϏbY$-+3mج(4D29_{ 1*r@1:V!9/TNz~!7O*0 J8 Ȃ];TTU#='(N*jHL{O)g_eΕӸ|&QEt6ɤ.꠫hq~o~e~S&>W̓oA(;7Q>5 Mȏ>ZǬY"v;"{yJ %`H n}GNNNQǕmCn"QASH$ⴶʼnFL$5qK#\ut6vL.o.O&8ZmYYp߷젩hU$;s(A( &4.MHqIUl4aDH$t 8AFg~w+l4%1(fP$jL?DT'Nȃ>L@gg'>h/| ;;{H4J&L)ҳ^E$8'dF~U?u;/~yDbHCӴ>a$ xɡD"A(ʦN6a3{T\.ZDvv:9 drıdesE4bj\>'erDZXD#$):N%G4ӝa񸑜 ^~ ֮ٳg=X6t4X$":* FUU07>d2^`~ǿVUDIr>>lqyYl)(LYImm㦔Q8c!=#&Qttdj5 fTM'UXOss'MW۝٭?/:MRS4IhDShDl\qQQ1<T`ea/ -Á$ݙ3F$g_yv79U%KCI j繍/D:Igȍh6rճ۸j4 i"7kbSOG[;H]1DJv0H v:~~WP8Hw#'XbqYTGdg! "ɤ !0'NM`s'VKDQv0c,JH`sML2 SPs4HJzy|6M;f@E$ӑTLf뼛,8]Dp}\wRҝ#ҳQ%IUʯу'}|/QTTVe 8SBFNƼ ow׃;ZDsh0զa5Ɍ. nFJǖ0 .x\g-N\xjww7D'AuA.ƴc t EIJFebR{.N>1B{s#zKh4`9zu̜3goל8h8@0҅$ I0L8f\1jE\}.b*seyĕc B$ASF$a7DTh4F0fڌf:;; c2FL)2@goź,n0L&3g̙31L6^EB3\|t;'"$Yرyy[.+Hn1Pkf/nQLRy$f%44 ֕$g2|-^BF>A4r n+*.zr352v`Eqc|N]=}{?f,dDb ܲnƕqhi+_`$NP8q0~ië:]{ov'-a (*ZEjgj8uθtqX6-jQ [XC  Yv,?n%Mɓޓ9,߽0f@éʓ*Ec^ %}kj4FR4v/V3QջQ]IBS4|Zfκ#Fvihh4y=?2iV!I466ͽû;0c8,Nr-ƺ݊A8+NspAsu>߶NtUa=y$3득̘?0LO@'{?/|%Z;9:SS:f$ *c1tV7.GS0zwS8f$)U'$++QHIKalH$r׳rH9!u 4oe۶ Q16T,/ +PQQESuB{Ĺ+L9. ̛;v.Rl$R{x|嫫:{Ƶq"e7"#-1ƐŇ>b""hiM Ν9sMSP8_b%5tlF1[v* +).) hjl'))Yxä&_˹+l.<R$XTl+Gc$ <9k3*KJJzҥԩXplN}7&2}n 7ϛĖv`K$0gkdN/^/BE<|~ͧOpXɄ|qyT:[|AӇȘMF ? I, سƓnTQDId;|=’QLZ8[UǕZaNLo}y6$ RVVm<ݎ Vxwɓg#w|vT>945BsS=01#; Xv)Յ`L–UH(|V7#e3g0e 8yyQW]Gm]3MM< fF (ـ˨ I0͗h9xILH@tθL{%|+ "k]E/Q2L/dnV33e <}s_?:(berl浗ɿ{gĥ+d).-|g9>>S6,ľ}gQew@XUٶe7g!d%#' ˏd>a__dL ͞LBRoԩSHMRB|>*+"eĀwfqݜkqLE&Vk =?NU5> 4MCpڧ0Il'A˜#5JM@?ZkԭKjii3fzL2ɂGlo#7FVNetrؾsΜfڌь,)"3;;MCY&oxrqn3Qc[0vZ0{q{|'e+F5vdndL>a]'$tR}!pc,p3/%NC[PD@clZ4)tB]^럞( =HAq(C~d?u+C9Qٶm >u;-V=H홣 {)N_yNGv+m, &E BdMĜoFd1CdioLYL>9Uxo粒 I$C8t Vk2)ii H$TNXm~vf#GPQQA֌%w SPcO4؜yQ3Wݖ((((5jkG(෿}'xIYprARJž#51=\Ŝ;o Q%# 4B!?.A?dB!w-MA"I^˰ƎŒH nœLHNhj_HExq($Pu6}H["|!%:\T$Iu.r=>G7Ə椾Μ:{?ӦDKK n uLu{)ͱ1( UyZ$ɺ_00t]Ge a2wX,"&ƌ)p~zZ[["m7D>B%G ֭vs]w|?Ey ߿ GG8~^-~ڜn?~&i4#}~~7Z(z:{]~m:: ;=4^iJ/)hibINlRWUǩCr}SJ]]k֬0")i]W   .]⭷bɒ%9v╗^ b4o,fp;ivn-TU&.j hj]WQ54T% 4Tt4B ANXёdVM4\J!_Y2>زeK3f%Qgƈ܎+n#ќsMStQ&LC(I㨛 "o6PQ5%+7C}NtkǕ+Wx7?>ӧO矟,g7HϜ)Q&3u2QB6(^"XhzQA#5U ua-EDt4|~ UP)*.ΞBs]gxh\w>E4$'ʌNp)2k6MS{=]6% ]7.k=4M% aݘ0YcZHPda0~:k%x ?fzo~m/^"\"JKKˋ}6^{Guay6o!&dB8#9@Ʉd$94DIF  Ua!F (]Zs`= 1o~0qrX%.DZ5] ڃHU~0iKt0BӰڒ0h@*m:kOQ-^' % 1MX3lIsL#ᡃ[o=Jyk6n 1 $Ib…=Ûo'>40d#KC݈,##C$tT5L0 hwpuiil΋-#`Lɸ>ׯSy&f# @nl^ fsMޙ{)D5WElr+24b49qk׮fpAt]sf͚AU !z Ȭ,ʘ1c3f̢֭[&*vA+91{j.YX-vlV+D `OGG.WOnNn.;yˍ8v. QT#ҡȈB4/CMA|h]Ը^cc#> / #CD]g::fs>$Yfa0?>-c03 ߿ӉСCɷc2~xrssYr+W"s Vɓ\+@E1$%&lO8;i?I))E/\9<'?2'":'Mӻm Bn]HnWf_5^. g#S+㩕ڮ]{ю@-[tRî]0L1{>G[[?y5k/ EF,>|8999&j4Y1MB$(M}}=\t Ӊ=ԇT"=1纙fi)pM0<(}v1I?']!(w9Obgu5.]֭[Yf +WHw@]ug?V}}=޽Jjj*Lbb"fY@ 륽6N'mmm|>4MCW,WhVE끼V` %4D_gMSQ./,B!kP\N Gk[>E˖-Cu/_֭[5kgn _!B z\..^؇93mܒ$)6^$D. dNjj^TU1@4 / z+$Y{*,}ǖ-[={67n$99~?ݻ1vȟvTz(Df,F"0{+ω#4UA4U#&`61i 7 ԌR!I+V //Kyxg~Fff&_H/zD7zeӫJdki0:ML~ԁaflƄ&,fep`D7=Yw9Cƃ>H~~>w_`<쳼i&233ԁj@AaF Kj#2"{w{-Zf؟+/brrrBUUF#7nAHIIaɒ%n6l)S$ȲH/1 H(0 qlV,bt;'ViDȯx!՗{Jo{ 0֭[ǪU 6,fΞ=ՑEff&bXtr=a8NL&yyy3gرvl6yyy3l6L4o<Ο?Ogg'yyyA~?yyyF#-c#96>*a $o&B)xWU9M/;+wrn\>[fӘZ`zmY-2$bSFéACs@݁i&&MyGXn=[oeܹ=Ļ(~6mD[[lٲO(q].@ذa۶m$^+>|8x^c cjhƤI8}4;w?TTTp 7`4 B];Ns'6Lcÿ=cĿ>^s3}[G;xǟ-c$ ~[v`WKd`w,SNeƍ>}zP`0;CBBgϦnFl6[LUUnSZZ$I1@FFFt_'|‚ EIPIK2 l4LMz B KˌnBD(i] NVNVjjzmұ%`%*0"_͂0cL_-cXdIL|^͛{!1-ZDcc#>,/aAQTTO?͙3gHJJ@Q._L[[[JUUE۷ɍ7ވkŋ)++]'=ʋD%Q(XA'/-) Z>O/3$?zU DsţQpFToIrrzhk/l^|իWseL&gϞ:V^%11JjjjzFZZZXz5NMXz5{/֔C1R,$a@`z4(6R~,tm }{A׍F2tt>Vfر}[Wc#jctwWx#Jyxٷoǎd2i~Ƙ5ӧc<餥%3V,pBÐe4S TWWх e$E"AG%i?) yHnF61y>U`&, )M0ٳX,,[Obij1_d޽|駱MhR@w5/m[1FI2UUci(#Ij[1& ߄UU1 $~Zuc6눪j(?ަ FZ]ډsBk^  9B*K.? u6FCCCƈ#q-9#3gn#ΝiӦ!2pcǎt:7oٜ8qĉcL2޽{1 ̙3Ξ=ɓ';v,eee8NCii)YYYڵtg8`t 7zݯen3i!I~0fc|qm~;_4"ӦMbr bJur"+܏;ۻlw2iwueǹ789#V$/2yd6m/%U%Qc͚5<}M2*((ot][oʘ-5xjw-j[DyRFA=A?z{W20 hI&6Eq\eSb݊{>6FwkժU=Ͽ60*>+W ݻÇ3vXdYѦw;M4LxnwsϊjԙhsqҮ& (zJX!C Z] Ӭ8,z#rpEM~ #3\0 2I*nvo=dddpM… hhhIHMMdŋIOO$$$0}t/^Lrr2h"2228y$ xqttq@aHkk: m6 V'ӦM㞻&~zΝW>LĶOo|Ap8iJ_:~~Cf+ޢ7%_e%޽;4vvISS~Z*++inn&;; lܹ:Xz5GCłV5f9x tvv"2~G Ja') =jp l:RC+WqߣxwcLsIDATs W]AV{}<ÿ*?}7=ÞŀEM2?C'#b$ sTU6S$я~t]`065;a0bjTQ# rMp,.}?kDD{.J+H8p ǠDM?V8JxMw]@ `HIIСC}lܸW^yg}MMCm2fBo;țoO<(1s߾},ZcvرccRe}aGDcqzHVSH ! ĉ7[oʂ ظq#/ذa*9~c AE*++y뭷馛 ?oIy駩iiѓ'cZ#Wǣ*J gs2cm +*ƜʈY\k^ m_*Z$/k&zG B `Y4y I2_o_sўurr&חV)$u|?>2hX4#<RT'nn@gTE)4zRyŒ;tݟ \|`=O\8E :Q=24V sE]G߁rt;)BJHm<͖sreOX'1B?rF-wӪlx]_=:9Bt^ڜMQpra0:䍟>i'蝤PF8Hh]6W-Z5T?4`橳[Aw7?ާF?E8O0QgaP^[ȓ_CYQ_L|'Z?~?j|M,v`zuGePF>mgq`rZ1~ߔo Ҥ݂k\C?Z=ٺn!+k5pǠAuݝn1o pm-={ubj'[Q}mW"78%8B_7|\yS0R%zg37Eɂ%y!<\_3Óꉺ&t?qZ"Tl(ƙ κӭ]ΘDnJb+c]鲧#l_w￀dZ? QQz+}T7  nt]E4Lf1o ڪ:ͩNkKs!LiQmk2cԙhLt ]+E0M'JO\Ew TWpW۹yܐU! C&ڲ`0!&}qPOB$("(   *`4 ^tKb\к;‘=$@ ILY~#'OqJW&b\ϰ;=n5p*ZBZBVZAh7, &3%/ # #) - ( '# 5 ;=5 )#9&'3#"#,--)()4+('-7+36/5;856:3/1-IQO q J:T-b$f* s. r#k9}>m5D4.Z>.O5,n:!c4 n-4S'&U$iFBE5^Kd(*`V7 !q@lv̭:YĪNlcL *C<;UA!IQl66N qĚ.nP`/뫛Mɣ~0ںWSow܀h TAR_ALIqOP C 2Ց|a6v+4ځPp<>%-A5C)ȼtA K<3~8 8I !Mb_TɲY6u\hB"Λ߈368\sZz 1z@yYsnhM7,!@,dZaҰAjM1|q}T wa+{6PBG Uk`.@Gf1A8@3$c3D 0B0@20Đ& (jL" e 2dp GXfv A8s %rM6w~shs7o*Ÿ/)s8$%^7HÍ4ӂ6@ 0qp lI @ c5+zX{*uTiDwxju6eGP;ֲ1ae؉7^R`B!P# (HP8IbԸ%rCA3\i.&1D1l(|!,(|"d]PapCģ7 mnZ\v\A zaDZ }'@h,*z0=L _`@jxÊliӊ6VdS"@ U6@ d 2k4$81"vH n JG7&f&<,]nń`J(RM/Fz 28L G`o ZQOv, 6Q![K'x y %A: ˍqCERy^h # XVx0^|N4ɠ!B`!N6HԆ3d#w2xE.|\47褐3 {s!*~`Uj</(²0DH ]A Z\yW囖`#-QZK@0!z`X!:PA`N2GC 蘃4|0!- +̭C 0F>~eC /W`fc)"^} śMHa.;'R;3r7FQ ϧ2N hc`ւ E#ȘZv؁p ?T( Vq_W 6 +_mo\C(T65T ,K 'ՀGw (c3{x,&a$ əDt IeHWk$YI3Ȑ!qAЋI]`7h>F{KQXmU+BaZz]ȭ}|SpA &dĶnI ;a= ܇rBFԁ*ɢ_5"AD]CSBlt Qxpt !G* ^)(>adΖtcX aR .6JWR d("mDLKGBeG&^]^ Z([ ^; >*'*O=cxnD I@nA|/I$p(OW0gN8Ϲw@ЇNHOҗ;PԧN[XϺַ{`NhOpNxϻOO;񐏼'O[ϼ7{GOқOWֻgOϽwOO;ЏO[Ͼ{OOOϿ8Xx ؀8Xx؁ "8$X&x(*,؂.0284X6x8:<؃>@B8DXFxHJL؄NPR8TXVxXZ\؅^`b8dXfxhjl؆npr8tXvxxz|؇~8Xx؈8Xx؉8Xx؊8Xx؋8XxȘʸ،8Xxؘڸ؍8Xx蘎긎؎8Xx؏9Yy ِ9Yyّ "9$Y&y(*,ْ.0294Y6y8:<ٓ>@B9DYFyHJLٔNPR9TYVyXZ\ٕ^`b9dYfyhjlٖnpr9tYvyxz|ٗ~9Yy٘9Ys  pəo )Inimɚ隱 l)Iikɛj )iIiɉhɜg  !.!matt@eden: ~/tappy,!!matt@eden: ~/tappy,o8p *DP!AhP@-b@č(;D 1v4lTy))L lϠ;)<*hM J q)ԠC1ZPV!!matt@eden: ~/tappy,!x8p *DP!AhP@-bgF ۗ GjE0w:PR"oI<(]nOAUAH&:7t0>珔I G!!matt@eden: ~/tappy,*x8p *DP!AhP@-bgF~OTZ$$PRTLэsHEؽO'?2OL(a"ͽ?O Pϟ?sNb*8aY!!matt@eden: ~/tappy,2 eH"paC.4@w1 Z욬XsR(`9LwCl`y8"`+_# .\X}h08ubD!*!matt@eden: ~/tappy,;-  L*D‡]xHqaĉ^F9RQ̼gI~ۗOT,y2̚`0ݐ ŮɊ5.H I^-z4Ԫ+xQ"B)|˓*X4{̓~}Nэ \?ĊI^h鮿*vGBS |:CTd A-j( 7PQbEy@r)e;CkU$Ex#9!ϧ_!a!matt@eden: ~/tappy,h !!matt@eden: ~/tappy,r EKˁPB9TGA7A.lpdÇ$A2O@ Pw !!matt@eden: ~/tappy,{D@o9$%Ev9I؁ ~oߠPIޠ%Pmf4鏄0{!"!matt@eden: ~/tappy, U Xo߾Q8KT,S y pETPG["(󈑙')y*sL@]k'xyF߾Oqt7(#$П 5?! !matt@eden: ~/tappy,~ Hp *‚ >QĊ'RĨ!G~ۗOT.ȊI X@/Sb'h"QN4ӡ4JVHsOUH3hI>ո"ƀ!!matt@eden: ~/tappy,{Hྃ ,X0Å :L1D⾅]xHqeKE<~` ! H=)1((RoG҄  ƠҠG(ǝ],@z;F' Zn); M(1hήBn ! !matt@eden: ~/tappy,Hྃ ,X0Å :L1D ^WF%n @w1!$-vMVGGR )'7G)q7^)Ɇ# 7Ny>R4> "ۛo7!!matt@eden: ~/tappy,Hྃ ,X0Å :L1D ^WF%n=DE[났hˍ2UF @kF"ާ;N'E(Y"ͽ?V'bUϟ?sN }r-ŶQn !!matt@eden: ~/tappy,{Hྃ ,X0Å :L1D⾅]xHqeKE<~` ! H=)1((RoG҄ 8,/{! !matt@eden: ~/tappy,  EIoߥ h" &Pv0!!^ &;>MB̓F5rc3! !matt@eden: ~/tappy, E`>sL ŮɊ5BχB<oG6ҠdR(_5ͅ=!>!matt@eden: ~/tappy, L޾|(pk].( שJ7Z;G7 Pa>a8rsN !!nosetests tap.tests.test_rules,!!nosetests tap.tests.test_rules,\ySg]!!nosetests tap.tests.test_rules, \ySg]!!nosetests tap.tests.test_rules,< H`AWT(( ! l`D1rxP8z) <@01r'C. JѣH*%SKf|JUUT^ݚV]~v԰FEv)۶I=*wnѺvt/ߖ~b , #!!matt@eden: ~/tappy,%uEH*\ȰÇ#JHŋ3jȱǏ CIɓ Dɲ˗0cʜI͛8_,ϟ@ Jя; ]ʴӧPJtԫXjʵ+̪KٳG]˶۷p!Kݻx˷߿50pNXx1B"l< @BȒRcRVT:T&Y$Qzb%Sc.eJ٥HaPKRM8*jUj(B*:jc@A.ʩ,\z*PJꨦv¦jBjZ 몱j믭Z+lꪲIJ):m2{+ km,K-ۖjKknvn`R% ߵWG,Q=LgqLo LQ"l,0,4l8<@C j4tE=P34# uA5vmuNMFȲ"]$3 Y/veq9CrJmݵ o;3QسO>PoA9^BZj,OA3l35ȟѿPP'zEq;|˝M&yDXJ2N,48!`14-ф- {C% )x4C~Q2B-0ٝd,k sc 8&'qKq B P!CH}<^Q0EҤ2I@2xKZItHZܢAv[(ڟ SDZd:">hnJcT.}Nm!`678M+8w3 %-!F0v+Ja;XhC ˈ2 $ikzc"@r DB @ U˰ `p} ¸'`U5m@ "!ddbLILAv@ FQѲFl@3gvle`N hB:Qa^V_EKN" gp7TA#!2Áf$ ),`jڥI s0*łQ| h\U J` Ҁ\#-n"1BtGϗ>!(C`zƙ4m1X@"wK]}9\.UGUBKjΘ> 7vB{ܻ_ 9枇k[? 0&R}料蹣=fÎ ZxL*[Xβ.{`L2hN6pL:xγ>πMBЈNF;ѐ'MJ[Ҙδ7N{ XC9YԩMv;Y3S-Sr~}E]I@ӷO}S`I#CcABHr8 86d8\sDW*Z1'bI8?`BԢ 7f @,DrCRG&4KuK7HX%fw{QGW%t% `| ^&lQgf~D0PdF;H߶#.ep#BLr,. cy^&9 XiT' "І4ʧ@=Hi w!H`G5D-G8^ĊI+HlP* XȆ8 B@7?87HʡB@;yS,шХ:Ԡ! @ tpt\ 6i؉sl' mѕ)zPU c>AbWQ1jpDB,Bq#q A<3Ct|7! '@YH@E%)dU"Liq1`% !}' v`IbtkPКD=HGA{ OA sQ2P!NaG^|h2i;nz%fc*0sx/a81GLA\8nȖUUNȌ 8T#H lYd#~ܜ %1u)C䦙, S Pv.ehNh!\Ca#AATjhmcW1#$id7@D!wUIE꠪E-dlE9:* - aOhc8 8 NfS|'?ة #mT I%4Q;@ (p!K)*yQQ9" SuH:ز-ῂF!!&kxA<TaH2ap@!֡2HVaC>a`5k, ]4!>FEJ9%bn35)=QZ$$:UESst#X?S F i>}!!matt@eden: ~/tappy,2m| Hp *‚ >QĊ'RĨ!G̼gIAbd~>PF' y̽x"ݐH8zypB@|bpsiF" u_Gf5=1 !;!matt@eden: ~/tappy,;m7H*\Ȱᾇ! ,Xŋ#jHq Ə 5FɌ"NLBœ  r.ryK4S̹OkPM7/:<ԟV^* tC48&+ֺ&)\$mܹ}h1؟R%8Qyy* ) q"=B.FFSM E+]ATOap "'t+\.Ls5'Vypw3@E w4$?OvCPDm5ͅh( i셙?g_8`H ;cTN>BCTjPUXv>2XC8FӍJDэ8fWG<¤T@!!matt@eden: ~/tappy,hmHHྃ ,X0Å :L1D ^WF%npdHIjR$˓Oɐ)my3ǀ!!matt@eden: ~/tappy,rmO8p *DP!AhP@-bgƎ5b(`G _;)) A)gN Fz0 !/!matt@eden: ~/tappy,{mO8p *DP!AhP@-bgƎ5b(`G _;)) A)gN Fz0 ! !matt@eden: ~/tappy,mHྃ ,X0Å :L1D ^WF%nS"(V(GTQxnu>}ʔ T"ȊA)P-!%XI}>mi /f 0Ƌ 1 ! !matt@eden: ~/tappy,mu8p *DP!AhPzR'P־E&R(RH `KI& sx3ˡ>XΆJikґ;՜BH4l̀!!matt@eden: ~/tappy,m{Hྃ ,X0Å :L1D⾅]xHqeKE<~` ! H=)1((RoG҄ QĊ'RĨ!F 3h H[cE_rx05A3# +^xt@ )ҟA ;TcQ3TB.$ aHjaSg/b !!matt@eden: ~/tappy,mHྃ ,X0Å :L1D ^WF%n'@W<Fy_ebhN^^ @C3O77S-UA/ H SѣI'URe@?N]F|?: @;x!_E<$qdٝu/!!matt@eden: ~/tappy,m_Hྃ ,X0Å :L1D ^WF%npcH 0e#˟?w:`: t&TIIjT(Ƣ#O~ !!matt@eden: ~/tappy, mx8p *DP!AhP@-bgF~OTZ$$PRTLэsHEؽO'?2OL(a"ͽ?O Pϟ?sNb*8aY!!matt@eden: ~/tappy,m{Hྃ ,X0Å :L1D⾅]xHqeKE<~` ! H=)1((RoG҄ 'P UX!A*9T:R2TBJM a3CՐd)}c@!&!matt@eden: ~/tappy,7mHྃ ,X0Å :L1D ^WF?*f\a'$ L@̺aY#cfeggUUtcϟ05sB֭V}^ȺB̌7vl-!!matt@eden: ~/tappy,Am H*\ȰÇ#& !!matt@eden: ~/tappy,Jq EKˁPB9TGA7A.lpdÇ$A2O@ Pw !!matt@eden: ~/tappy,StD@o9$%Ev9I؁ ~oߠPIޠ%Pmf4鏄0{!!matt@eden: ~/tappy,[t U Xo߾Q8KT,S y pETPG["(󈑙')y*sL@]k'xyF߾Oqt7(#$П 5?!!matt@eden: ~/tappy,t L޾|(pk].( שJ7Z;G7 Pa>a8rsN ! !matt@eden: ~/tappy,mw Hp *‚ >Q. @8 #v!j 1ϟc iJ=ȲeNrHΐU" +Þ!"!matt@eden: ~/tappy,mHྃ ,X0Å :L1D ^WF%n `/{#Y" eǜ@kI "';N'(Y"ͽ?VbUϟ?sN r-ƐQ~ !!matt@eden: ~/tappy,mWHྃ ,X0Å :L1D ^WF%npdHIjR$˓ ^ySa9 ƠҠG(ǝ],@z;F' Zn); M(1hήBn !!matt@eden: ~/tappy,mHྃ ,X0Å :L1D ^WF%n @w1!$-vMVGGR )'7G)q7^)Ɇ# 7Ny>R4> "ۛo7!!matt@eden: ~/tappy,mHྃ ,X0Å :L1D ^WF%n=DE[났hˍ2UF @kF"ާ;N'E(Y"ͽ?V'bUϟ?sN }r-ŶQn !!matt@eden: ~/tappy,m{Hྃ ,X0Å :L1D⾅]xHqeKE<~` ! H=)1((RoG҄ Q .H I (u@sF`SB@%,V53Db@!!6nosetests --with-tap --tap-stream tap.tests.test_rules,-!!6nosetests --with-tap --tap-stream tap.tests.test_rules,  H*\Ȑy- PP  _Q320EFj;8D8 P}j&ȥ/߾sOj\ZP(%]_~F_匸j. -b>Q4(ꥩLŬ Z 4F5ʀ@hM!_R*_1L 9 U^؅,l[c|0+v :a(Wc㷯] ̼gI {SADCL$TmUDx D1]STP AmwE$ OD!`}cq&p@70j [pi 8CHs Q1#@@ ;M? - IL!E"'?)%c3`:^c -40 $8See0g`Npa {>4ב^SNzOҙBtLˀ,(P,HfmQ,dh2KÚz=p*ô.NJ?Y^9by+ Rrw#GW(Ef܅2}4= ŌQ\r2JJ<>$$cmKEՌXC "uiI%W-AH2Qu(_>&긢GW&?ڍ訦Qxb 饏ZS I(qH*h=ƎΎb)c0<3<)kB!zkGczZ, `;ML;:p4K00:C4N+qxܲN+˯Y-|pITH *p@ #/.,F 3pv  ( ZX 8*0%B CX+D[6PV04K;J Yn`S0G /vy}057Lm@%<$RRxe>0: 77 ϒ\OK 8 O t4Q^C$hRCI}Ķz?FCLE=;,4D ))*0敄.#59IAD AlKk^q0 ٠ @I0?qL P}\_ gN|x_Nj2}Bz'+]'#ZP"Xgl 񇵊-=}9\ D-`@p UD hQ :"*h$טB҂F;q  S  @ GBFH_H0 #qB 58Q^~ÕKԐgLe"tyDX=>,H1lIʜ)ҕ!*؁V.u@ aAd "B RWTz8&hL,AMh JU}6Sx=m+Yҳ")'lI7ڠy_wu]\:z %FRʏ>S! UOΐ+Ff+[ۘ6/!MiK v{*KS*ʃo=Ѐ BHz[͕fG@kHOJKӠ&#{;57gN󚳼e:k6r>`kLgha5uݔbÙ!|L>-̃Hmю/Y#Ng$"XGѱlkp/{uuʀA[.bvw.`VH)*3VEmj7/шR MjH3X\HƓqaXIta 1F IM%9$ uqcE 9r>G{Ѯ]Ow)otsZ $h:#q<w Swk~ZɼA8GAkc;d.wehtO_%=║&Џ ' >O@ _}+wz></ʠ@"A?0$G!2"Qd+u~(025W|( 8H+!!6nosetests --with-tap --tap-stream tap.tests.test_rules,Z 8P.C*\ȰÇ#JHŋ3j6:HȎ(S\ɲ˗8 1E a^LBG>u (E޾}rgϡ!RPU4N[%נtؔcYRMt׍ERhU J0}phU]]P@2na](+FQt YJ1Gc[5!!' Cpr ؆6]?T`-Vebɋ,S}UF4!yʇ^b!ǘ p @F MdB̬3C6aESГ-=$LR\Qȶ" `uRRQB _SW҉CB@ I?l uP_ [6&CDN O92@)ې? `j(2SF/fXkG )#c>ںpO(L" wRrT% cA2c$aAӎ OzL^v+շɸ&k$N/ӎ+b!$l3D @g4G:AB"lor"H *p@Bǰ Gw/3q ]tӷ,s8:dS(1d;! &U s0) )JY;P8\)6 lx΃,Yð/TYU3P;-/mw~.:3-r)̌#샓r4z3&Lg5CI_34ޟ+%*DG+?\<;P fOT[,Cֵ+=EDBd!{B2j."'H> 9Sf!`a OԌ PB`(ÁEk?h̝Y@^ v({ 5 _?҂@y C p3ЃI!E P8PO<ᯩ<$*P mD6%" H(+! yCćӟ̆D`,491L # IGC1\')2-\d#ep m+-T^s#j(sؘBF;q ygy lB  R(4͌R8SEPF+fR: F-JR3t?М pZP!uCAIS(6IP5ܦM ӌ8M#xO"%%|455TAOjجVl#q>Z2pו 69E41(]ȣC&@ '0S]~mvÅ'ekH(.]Km#JC!P)T^smAٖBEjYkQ.#بbV !(҅SNMj[ǔ0lak6wCi2N'Ib?GV ppDO7zɻO.bOp҃j@.Pa }>H  (CF@¤ M%!HyQ (Cb˦x ,ʋPDScY.C # `F5p(rԠCbfv-Ih[`9"4K/&ϊmΡ7>ח}~|( ~! N |XzHʧ̇{ 療12S|W{$(v—'ZA}.G: (wG>9X*؃~d) 5(0(A5Q6C Xu<՛?nO\$WioޙOpc>`:$/`t;=BPU^zag>aBi(b AK%h g?yn@fgP tO9PJI)q%lI-a>T,##XTזY֘6敷 qѶ:4؎+&rg>#L⡏7ñmγXo[n"wm~z_ǘEnNq*X .jpL`Ur;!wq.3^4a0TfD" K׀e ¸'IZw$pHg g~fq<tkg.D 2Zl>FYLέe 8;wD{ϝ4 bϤqi,5}gaO1] _2 e5IlG3u+a vV`Qq1OCE]+!D2covI:=okrrŧIFT5MO$ *1ә`RH V>'G3h)ü55E!$Pht ԨK$}4ȩ*Q;AUvG\ ut>Ld?ǧd~K/-p#=c=Qb(7}EF`@wd׭GkՃH#IۍՂX>+$JdH]m(;+@;$E(Iڃܼ]JA@GB$F"IPA}ֽ2Գ[SJ@оWEA4K:VeVdcd5.G(@sBR!VaVW}C61mPUQW2!OV O z}$8%vP[OH:cU9&[{TX ^4 WL.Ťl!e=GzOėpRFQ c0MRb\S .L=exJ5EǜAY>z[dɃ-)+v&U<`eDB`[FA]&]mҽÂj6`S&;cBU,:&fN`α1 g dn[>R&'P].$5Ydŏ!g\R@&_Fjb:0-pݷfmiWl;˖\UˠoԒ^酶:7mql&GO. ~ a1SoF@:&iז8rpX@MB^q9qfmP+I8rtoz5Ur:kqٍqr,EgZƴ#KtU\y_Q^ lKx#F:9x{{ e!vx~@/tnMn_j̳]m|8uP(Na 29bJ9+(wje&˰vn .!U{^ ~OuGuʧ/tRk ߵاڽOtF!g Pa#DF1"g8DRJ-]3[hN0X]- e@>r#Zxᦂ?hX!=*dK;H$'*!)jծ- d$-`@nuEرEQFV‚&J0-wL\Ev=(P59o^5GCC#bRaӵs[o ffqΌTݻ~N^Tó}6E+UZz+?P{e!ԫ&6o1[ܬZzv(|@nFp ,ˆ6x@xgYl{|( "%88BTb3 %3L1$s5ه(JE(ҀU"p騤 tBBVF+rݩ*VfG!VRfc],pR5, a]Myxa:ƙJd Xy8uOJ`f})Tۍd;7e@yOfm߆;n箈Rr]# Û Ÿ´oG\JP11LA=; Q&] (hƛhG<걎X+2ǸRq!:#>؅Iy>! A}tJHS\ьdVB\i[%-HD| OB*C%ۂald'4` ]#G+u1ɸ%@  XR^FWG!jn*4/G' z\ :Ѥ&d\irMݒQd!0Q@RAØ.jZ&t*ibJhb+rC(lUSZ+>(&4j(SB!J|.՟jTDj5|Ԙ4DJ/aN V(fD`R:YŧRIdm:QȕGXZ#4cфմ({/⑏Z^Jfq:Gϧ(\% ">_<";,gZ:\{(շe8f.B9<8>T6n)NRA$:ؙvv]$^ϻحe:ܷSD8@-J`P}@7eJ:3B>as#))"ӏ3x/DZKԩӋ D'/tWS}bl~yP9d/>U`NX=Nzg~9G^]#3֣[h? ?$VT5Rڿ;@gJ@C?ӫeS3ۯDv88 |[^:;* pAy@3 C39Q AKS4 %4j[{.O @3ؘ+e=Ö(l70'Tp 5lC8L 9:4;B6\!ÏC6̐W878A>c+8$v?>#6M(Pk[\0S3RUSq=Y.*3@pKZB]@⧣%Mb!F_H1fXbȎu>o` &ɫM #Zw8x2/♋b؛91[VJ8Hēۻs 0cYF=D'^ӻ J'j9CE&S68ZQ:#M[:+M;"cA檨CVWUduB&`ֺCI;LfNU c֊`f3dk&D$V9:uP͕5YmbX[dC@wK=D~g >~ƐAoӈdh?e5{l4RQT^U:vb>[:>AJ @J=&KЛ`p.V@bg? 闦;w@sekUC<\dx$Da{)>E8K`+>PKQ̐Kc :H.n^,cllfBt܍y/܈:f3fQˆPCH mElʚlDמGlm@$씈C\lJL!{6XZ nR4 :dn_2HVJC*̖T~YE':.]@-f h|  YH(U4 oŀhGT(sliu t ieFN4ǟ܏wHlRKq iGL^I}TH~c ^ c%p ) +yr#|h5]QmBT siM܄\Q}1p Zt~3`hsLaIșFKMm+$D9'LOQļˠQtHW܌M=ߘJi,Y|mV~PM{z'[5b8ŁP9S %6'%Q[7GWgwy=gњzysj!rg2.M%WST0i$o,Ǔ3%z{) ih (l:3`eT^%ӧPOwk@_]VV-G#Ig$&LͤTB{2ɗ[޹X-O$ç废i{ῶ10ո_}Q|C}$Vԏև&!&@mȂvVv'\iOv 5'7JcrmׁYYU *+׭Q,TTvg[]9`E!T8+h`.,8B޾}+}((̻}PDhP-Sn"#E4xGh`˗cV)d '*?.U)ԟ9<ǐ#Zm4T%Mj.c@4KD04@>$x#dawVAYk&ؤKBvh7eܺw]$_=QֺƈgӮm6ܺw-;'/1W/ ˽{` !  &+ֺ +Z$1Pz[Ǯ{G*PBB0L;9T$>$3Prᄞz칇B0P2 -⎁ ̰CCm ,#G $L(,oᩰ=>4w]T_| 띘"1hZ1y_wAGy^ .$ږǎzfF|\z^*qD&{hƩl8c?xҐB9z9Dg,dKxY&~"c?饁hŨ2PD i$ũ>Q+ ;,:A 8IoƸ 0G:$=V bJ?qm]nA0[WՎ'cDC{~ov ͽ {m|bf ?*B‹P ?AbұnGzbnIbm뾬14A>&/B" HQ1±Y݆¬#Ԭ@./>*N+TL4B (du}7޿G"B2mU#.mbFT\aFFB48rCIFAH#/P?,z_ArZ-d%Ġ:/,p`)VdyAO?(mCyDSn9Es^P93Rg~̋$ڤ 8G?ץR?'^CitTB>` S¼[ދ$~sbB[ ¸'F _씁 @NpIFD" .! VXqEC @@~GO!jdO&qvH1AJ_Eڈd& -1]3´1caK3x'%ɨJnr@ TFY|GP @piV(iR37Sbgf-lHmXE!`'ޫ ;rʂKqcX!yHzgک)D_%Wag.qI2ϡp#:kEB rMD `p7G:s8yԥ) 7P!.WLNb2f檦yZB'DІnXC`Fs8Tw #G8P49u>9(`έD*r&\*׊"8Θ a7h\t J` Ҁ 5,`y>, 4xs }ld#!eLYo`NnecHVM -фT-h6X%ɨzm뤮nAD }Z)O\edE}ºec+ۄ7,lar[XZ6cW[%QL*R`7v9^E9($Ly"ep{;Xö+\d%@ LAfn&C&/Q4$5F$N֖qy^&gA#6=~&r@暜y6r3S1n^=; wpcmB ~0Hh:1 3)`D,g: 1G`qiT(GeAh΅;|}e9$HȜgO[ss"~4L >ܫ;ț:&$bf7<2rl4͚Aܪ bC> .83.Mi>/}:ֿ>k>ӯk?Ox˿& !!matt@eden: ~/tappy,3$$8П*TH‡6$"É-j40@Jyv#;<ǒ) 6dFXs Di B$΄xof>mݏMj(zsje pTU0ѩnb:6S7\yS!!}>Uc(-JqgJ"Y)mBrcsѥq!A^({ [fnݽnpŏ+O9sɏ?}t_מ}v݁'>y!!matt@eden: ~/tappy,E 8П*\ȰÇ#JHŋ3jGCnIɓ(S4eH+cʜI͇2P0(J [~$W7 @3ӧ<,55TDJSJ* P@o NR J0kۛ l@@U^ܐE`@&dZ!U' ˚ lC3UDIʫc*@(`7(&@,gI&In1@mn؆H l'ϬCHޟt Cf)Ju052'/ Q)tQgalDʅv(^YbuPMKh%4sZ K<3 T>3R54K(c$:t-$ԷC2Γ#68\sZ1Wf?xm@6t͞{Zby`L74w{Q`%TGtEh ǩQ?G}1\[thʇpHlPfb1$>h! $c3D @0/3Ġ0@ʜD2 3$7<dBq̓s %yr XOd鍔G8\Sf"MC  M5ЉsrL'4Ǭ&6?\H}TzwG+zI[zGз yjP,ɐa'x!+z55@"ZmЌ'ܭ| ivr%SI68 {&,1LNI & d|scoS A=W$O͎zdP 'AQ@'4T5H0X>Y'i"6B UZы54x)1 y+">JbxEx-=qAB@>c!-On駧P9P#~ @>JZ@X[  .NF|*!a4N ڠhDnȁM ( 1pAt8(&zz`h%N"M߰ \I^^-iӂ> #3mgw?0yJfL'NtuZpȽkLMڢE(*Ps&/6 9yUD>U*G)|bhimOٻ>:)e!do^B;-CU1O}]GFn/ vg|9`}=@>ș哱},vt1]eXr[u>Gj/{E/_'?Ɵ`'ƝC"⧬M飼|1Vn,eM'F?ףh? }σG 0@w|B6Jl?_DPUɄDJ",HD"q/Fc+"a1zP_-l~ڶΡ4PL(~096V5 5jbZ1 p$dyF/Mic_yvAر|[ =~??$6k2*+ǏqYowߔE(oի5|8;?I6h F~jۚ1{,jy'f4v;۾HUτO4kh' H#F4 ~%KѨ1]:tI&q饗#=Ʈm_<}g}z-[Pɻ[0sXdY͛Qe@#L&:;;o~eƑm_d"Ls,x}m6a_TUEN8J$I&I&>?!}:+̌1cm6U'sa-ZDSSk֬aL~>>nw}+G]Mzbq.ʪT xG$u;p׳yf/_ygpQ$bas6t]d29uNqq1>$DvCZV, &ikﲑ/*/^LSS<> ~/fl6?j*ʪ.OJlGvC/tRl²eX~=֭C֭[Ǽyx)..& UU6$Gfҥ 8r$ILԩS%myW ]ױl|+_ M=EIj*v]wExS ?{~ӟxx饗={v7c>(k׮SUU Y۹C`~0 FÇ&gjcƌnf]нil  ~ƍݱSO=τ vgdi'OfGO/^'Oj*233Q ˦]H5Ȳ̈#%(~2]Ì3.!(Bvv6Ç`ҥK{ڵky(--GA1KY'~߷/SLa֭=<zzz-{ѢEx^n.iDv@vwr.+~vCzcu\Q ;{}^]ߺu+cƌ;W_7-ZĆ =uz;}ńpuK$og})Syf x衇eϋ/ا毪b޼y?;g2]t 6l9٭FZ[[zTUNӞ{__wMUU7eaV+k֬9?chI2"W_~;[l9OM[nk֬ywޏtdee$o8U$" ??ロǏK%Kt˛kSZZʫMkbя ^YY&$I[ogAlO_͛Ylxu=]Ѐ@4 wIatk_k׮㭭~G.Ozj?gwaѢE݁칚kbذas!v-Fh/2UUnJAAyKx)-Z= EUUI$toii)yygJ9҃=P<ٵ7y 6h"n7n7Qh}ԣGw[{;+8\b$b A`Ŋlٲŋ7ɵtw=&ϟƍ),,# sN'NgGFAeмc2ԝInʪU(//gɒ%_Y`6l{$K} =E[nnndO_x}Q '7x'v! g? fdMKKcѢEtvv`Z[[{ٴi ,駟;L&ɲe)i",x1 [n0Xr倞^:[K/UVs}"-;;!777OƑH]vq׳fBPΝ;{%}II [l< =aXg$WV+B/_M޾dOxYz5_UU?Nh7ot+¼yzB^^>! hq\?Yo6vٝ<ʲ7OqnWTT>s&{}5JL}ZV<˭ފ vm=dٞ= p8X,9K/{}{{;{AnO8c rssϓ;ǎcw}MӘ7o;wdΝ,\7 ѣO?4}#d>:j۶?gvmq߲e -&>țoѣlڴ:ѵ ?}oz]E{EQl߾0@+wy{4̙3p]J꜏A璿+ߺuk6m˗/7" I8|y2a`Z9w*++{A(//޺,b9sI;Oy &M7=諾9;{3?פc<ͿqF{9z9¦MM.;f6lkM67G`2zxlY)//СCy㮝ٷo_I ]`޼y0vX^~nOU{s˖y>M~AΛF^.!lBIՠa6ukv"k:}x>l6; ?яqݬ[{キ a…Ce}[.?>@ +E+V}vV^=dydz-]uoGeZZZwEܮ!D h]{l Iy8x {/f 6pB} "=EE}-KEq1]đe;|r ƬY8v/bP~c0נ*t:)((Ç{A BP[[ӧimm%Ht/X>l`2x1L̙3甔xbbuuuA?]/@ϧ,&-/ЀٳUVgtwpo~nYaC"dbȑL83T;Fɓ74Q3"+3"0vXJJF3z?8SO_YYɱcǺWn 8޺ 0x' Uw*SUpJ)|xGşYПgTTT : qK{++yխl.'$8X8\ K68 P >HELJv)fkڹ=****سgMMMC?]߮_;3zo~;}to\T3XS>/S[;Hkuu$7Ș.ʃMYN<,BР-IHKK ?/q2.:Ҳ2E-cʄ<&epH%Pfb3 ">L}̂jIl<$БYXn1fGk{λVc{\[<0iZ⥗^$me|We!$>xWn/8Wu9s?1LqgGe,XнIӫ[__:nf]UN QD 5ȈlpݨD<>Q-"#6SDVø3,:~kEͧxoW54+cƔ0cz Y N1_'r뭷m6>$I:O%I ~>z ٿ;i$4]6hNE3;P~L&qtM$|gr˹RsdrKlļGQRg%bK$&5Dx8H83!ѤJBUQ<2ܲ2vjNq3def3rHZX[_/4M$ESM&bʕdffs-+~ ?5*AjAKԭ75glF, +ǁ.hhQU!b l}uv6B:-8*: )tA$+ǃEÜ-䦉8Mh&7c|nZɓsy 7QٰaC<#EsHvYb6t .w F 0׊hX$E^@N@i(qak_8%Z:krvU ct:&NB0"nE7 9Y (uHdImF2wl޼1cp7~7~qhngٲedgg{̞M*%]?.1I:8rd6C< r)FSkAI3:L @0nWD' 3GNLL.)*e8(-*`rQ.X]9~U[molgܸq,^_~)_\wu >𥛹껹R$$Sxr2ih3B4'Tq!+L,&Ú|FxuǛdfdc5dAb 4*6 áW+:EYdz,4Hįrͼ6OeǎN|3k,&MD8ƛQ42 'PHp$.h ũk-މ?P5 @0$ Qhni#=MV^NO:Ui*ٹ=0B^#Cҋ;݉@4j h@JTѐf}`\6s,wrd|Y͵O2w\jkk!禈KLΝ }XCk _YJ|6͠şOߠL$&KDђ"Ɇfeta*A_=L4,ݎ+? aS$9Y."A6oQ1d,fnCбZXdѨZ/Gqq}ȲH٤/'`WpWu]{mS=7ynoFKs5>bdNbJ?;WL kdeC8UKHt YZ[1ŠF/AUM -IL&+V`ati>m!$tD#df8hh Jw}DcaF撗 ) JCMR:W=9NLtXv}^o=.6Sw:HA4A[^EA~&>fXI `phqDJ ?2dA5U5X%NaIFcT׆5l@ICS;NLK]Ḏ?D4j6r%|뷲?_`J\lD7hIp( IDATf%`mG}[_q]yڸAG ''g]5MHOOg^U\ƈq9~`[oG ]a_L^ζV唖xy?p8w?d]lfқηeR6! YԶdf%8]VfϹS8ri32fxrmtUJ2ȏNjߢ9[;mC6%MvS6z_x,6##A, gtԓk?ׁ.MtI.ƻ6c ?N,x \_R[UM͏P6~$8ө>^ʩSM=Von;$Qx6Ii5c 1bTUeDa>ǙȑMA345%r`aC)AGD:idIjgyL4Gs8C1Ɩ0˃leX(_/K_5\EVX1䷸]5j-W׻CB̻[7 O'oc8Y2'%`6|eTUQuMtoArrr: cJŒ, 0u . fb+orí"{)QH{cRFcc;t x_FI jlCnYFpҋ"oD|~)z=?z϶u8fJ֔CY[w͎;z8a6O=ԠD"ԩSICC ^'::L>,?/@];Lzddp۾ ض˅ ģ1D*5PC&4y5|qדQl&.99zt+E6m۶mj>cb*<5Nvv6EEEq^ذUXAZ[p$h1wG'45cQv*ˏ1N;L4aٜmG96_q%ބJk1 X8U@M>t@%$DK"Ӗe'!$q+[wr4Vddl"dŌ7;w'uIw>xK3lp⫪Jqq1$曯S ^xq7'P45(HAi^"&Yxex2h9fr f\@GGRDN-' ) '"V^NS2fJ8C6HZ +pӆi!QgdzژxH&25ټ!N6B&mmm䐛K}}6|Jsk:1euUvk6KHII 6n91e(V:1ldXx_EKkb-6\i٨z6_;0  #x V$k!+kHFcx6,2=RI$Wņ_惽5ܼl7eM5_:EHc2Vg/u 96"f ŮIaDHϛE]cG*pqgP_UQTTDmmn>܋Fꈟ74HD$=ǎرc'Y:aÆo.]K FFySiij͇'Ũ|L6+{wU89lh:B\~Y+ﺉVFep8])+yQq8U]G6f \;՘LW*":qۉBD6qYUC !Dc]+2FdHIlV;2ꫯg#/&Ip<G" E|( ( x^^/d:8NVWcԜl5ͷa ׂa5a0L;1wL_ĉVĶH4'kj6Wz]h|D8A"!P]IUtzL8}`IM-Oa9 |;D݄bC5NFvee8ZGS{0p*de:(E$ W>M~Blܻ5.ZO;tO_LζX6vܭ)iZ徽{pyj&$`Ĉ<:˛b3!I0j8$)Քy=l~-DdBlj ÝBGc33W Wٽc7;$$7z kobw_94tRRRH(%T5M[Hv9&$Cq#h Æaǯ-mPWWGqq1iiij`4j?2ׁ,]@]:x&GQE#L`ٿKgsךPI>HegkTWc[;&;:,i,SVZrCG%&tĜdfz+pRO[]6Ɂ]U ٜǣyEcմzC8lDQE2dfbhatà IR4,'[eq%IEex<?QC433A"I/ݲ'O`yTZ%)pcø,al):q-&--_e``jfAOwJK/e<0zN׻^{8ԻdE$#I"f 4xmGNE&)!#FѨL(8U$5,qf4Uu$lba<Մ˴H$|NHJ}ڸ7Ͻ|.k컅[H|ďtL& 2e --gȯ(I&Oʕ+ebbI$hBn`@K@$EMPbaNn68|}aOwQqtA,Mʹ5s˵ù|NA bKi|q4#?5`d6Cg؈lkI4 A@ M $lf q0B3H2iy,."P<]K&pdƌEyIlf M"ۭ$g:kѣGim\TT4hQHǷcf ڻ:?VS%/~Q̃biDfxEQ z3]3@EQDdI% CS㴞n$d٘4-'/Yq$K"VdQGtVU>ͷSGtG+ v=v\bQXK"/76?ICj!"]$D܏``2cjB 'C+lsYvBet %Nfv:k8ЌE6f'U%]0gx63X\7R{/{&%p 558~hoO$rJA'^ۤaFnAbt.?f=MZV'q4Q=@"@5d#!r9>fbV.ȁDr瑓_FB$_]c6eDB#"K"&! 8]MggI$U4-A4.P_O~+&/0\yT>yAhnsՇ.IC4 uuu}lwH{ fse&fKg^q[xW8Bg>X"N'"Νk&?'Bgg'&O!l%/˂ϟ$F~Ai.4i!:^ze[9Uъ~x%a 't:++'=C$mvD+w'S KsiݘlfZZ26*,햑%k`yJ8O_zIHbdg! "ɤYDEXMƍ&?pB`go(0utjhΐ©zOI#:v4HNzW}U0ەRDQƌOv^0od&)S'M,#E)AnSJ9$x; 4a(>Bvf# f,㦛aTk?o~EZL\DlRTjde" ?EU2+ɥMbY,X4hT>AEdLtM%D:NI]3"L:J[[H_ƽD`೮ׁo/Bz´i`ڴiX,>;FE0MMM1+y\d9t'& $ 9Y,X[+'),NGE8'esƦVf_y/M{@!0v% 7o~w*s ƽN\1fgE,yT9׍Ak?UVQVvf_UVLUUN:EQQ7ݸ矿N/Ѹ( d%BNd3zLVsֺ,??}f_=_Ç"U%Ӯ@HgVI@k{[_z`Np11E^Y"X$>_z<n;Yn&HRTR@X;j"@\ME6V 9}t^4ǐm|^BP& ;EeɟN?^ԉݝJ$'O2g/XHF{G;ԯ$PAH``%z1Idgc7@n&;ql 2 1  vn3?䙕7}Aw{gOy7S'1wd'r D2|Tp&'sP1G~ p?zӌ%Rb´41ytz<ȸ ^Ν>M{{+"ݎGC@ :g4k:ip؝ԕ7r%j#(4ITX WRɄb3 fo'u1}?Fÿh4QWWG~;w[gwWӌg=HxD[;34\zKE8q9R['{IMȡ=}^LGp`~OOtNlßZ Q 2" OKpIllJR`=HA<8NJΗMll4('8N)p mn!o-Vk~aW^@6XZv˗館lVo}V%ՌΕ>NEKk+V3adH,rLZ(+)cD aH"bٶiRrzArDW/_j=-$%e+z 6LX>}͌b ;S_WKCU-vQAviNDL-6T#^$cEq(X-o.ϜqtKC!==^kwOKKRܓvZfƌL4Xm. n >8@`Xᘚ ռUVql$ ꏨIayY4`c8n:VM.Ɛ`DU%A(nJtFj4r Fdﶏdi~/durJ<ASmZWc ĉ\.F. IDATqgh4"2ggRk"udff/lذ+W ԩmC Lj:׆R]ĐX@M58`# Vk3 $O $C@+ˈOJƍJ"RN]M  YIHf!4">M}e5 \.͋O2PzAqTUT#"x"Fi5W]!y@"mm-ܸVÉE00چFe姻+^/u477zyog׽3#8w\*A?,Z)S2{L'3FgРDNZ'cm㳐׏)Gާ>~Xm6{|gOr ̉X$CS Yϩcbmm/}coBʩ(NŅGz[xqCy\Lk [ Z9~8[neɒ%444*,y7sO3;߳p"Q3ǰ^'-- ;wVR8E/Fx6.K3%WI>x~XĜSY|0eij4ac0h6 9ӟq{$LHlc9o~ 9Qx%-^%)11 FuNpPe8NDW&"MMCC15)ц'0SWux?G}DxxxvD9S|Owo?,LO@@sG(Ef(~HZ/x__x1Uin4Ȃi`',\ {op|ydi$ kdT رa 4ZBF:JsABXl2nNeiQ 5~4[^ gJ:k*UUTV5P_oڊ^@Jca8tODtֆxoiέ CۄwP](=Gt'_G3:kO?;wp`j[?M%k FI5Q ڟrx"bb"8r >ي#10- O W\&qPM!*.N=Ӳ{ͻJbr}'u{ 3ƃǘ1 u).FcӅFlB)~4,Kǿl=򤞮7s;(-?~<7ØQL_sv0bcyx΃_G 6>p|)cǧ2(?im%a@N;Ņ3x`X@|R Mưbucnbq;ȂD`h8--huLiC2n@NLH m`y~('/uǏZY}uk(̀{|3 ãs|nH%39y,CG/ֈB] 臤hv QL>AK`x_ F76s uMm}pU8q?YLCm= qNفN!44>}@Ԣ1T^Kh6n"`Ӝ9s)S/]vٺu+1㻼k<ďhdYDhJё43mxΜ*a'28mY͌m摅SA!ye mVV8FpHf+LG@H8ndv{}nmbGOۇjbqK %Wcu\'N'wߵkZZp' e -[xx'ZDx6{fD 6;H,z5>F :=LpIvZq ,6dY !1 .$ >2Gb6B%c2p9 M6bn`k2cks)l˼3FlقkĿa0(//g˖-,^_+Y|$V} D$hv 'b 6}`0.A$,<Fhz\0c(cs "vxp{]O+ihoQvA[˶0a$L&N׫ߪ n%II 7or?bԔ S@CqڪLfv6ǎ!P. KkA$ BdDc0D!E|.;NsۉBʐDގ݉W*kmXZ LuN'!Þ|&LDSS6m+Oo7mڄba#p1'?}}LÎ9w MZLVI1:=ewrXn;͌ւˉJ]uյ&vgGC*m̙?=v1dPذa---yK Ug*f'7ɕ|`ƍ̛7Aoּ"/D''>y"MH )1X}'d!,JA|dهO[>tH>d$N.+jih6SWFmu m\IDFI>dH^ 5f\U<3gV~X,DZcEFs˹ru{_w^M0օn̋_Nf[ /GUzl;;ìY8qDI$^/C%!!A޽8x,1 #;5C сNjc'<\GFEvmIF+{1<4X^kPtGd,))^_cǎ%??tgLfv=Ljx?#:ѣxR-Xn"}h4deeIG⯯Ote1DnEj$;AFrȂ,KX:Z-NZiAH ,[Ð>oz*|^ͳAөs|EbBbׇY 947nc\.dY&)))SaÆ| vׯ_feȑ?'lb׮vȧhk=Ea'<*`#a肂O`8hkklnR 9 di̜3L;477sYfS__ϻᆱYO|ߟJ,U3l /^=C|O& c#PY4qDt:ӦM#;;·~Nvɱc0L8N#36QԪ79w׮]jq0qDL&ǎ#33S;co~?;:uV`|>BCB[Mݽl#~:stUg&22K=dȐ!,\V^yW^y&d~QWVKN}|}J$ k.6l@^^ ;,w=u)APYY %22("""'((n!himtbhmmDKK vIj닂pQH?gO~/Lnn.frGE<vp1mgX "2-b׮]L8I&+oG/#љ6 LYY-9οCe$IǔFAů<몐~ҥ@GCZ-[(,YCr̙yh!}xj撐)((`ҤIl߾pz)F#?<1E͚Zan(a߾}(,]T'kɒ%ȲLqq_!p##s_^^ѐCBB ,`Ν]H__DGGbŊ[,B޻NB\nܸӉ܎ EQT;-)ˇw^s_w{M\?-_jk׮%..|)))B,lOLATߜ,e )7J)u3o7IIIW=YYY瓛 呓,cFM{{;$˗7n$!!`̙3={v;%d9d,Y֭[y7x뭷xh4wc֬Y޼<)裏"2gϞ7ngڋ_u꽦;wnSIi&/_$&&Fux'Mtۚ GUUDGGSSSCrr2c23gr2331L hllt~Z_%11D h$,, B\\aaaOBB׮]\.z=Cbb:}GLL ь;aÆq9222HIIm-$55Kشi̝;6֮]˳>Kmm-ٜ={6nc=ƻˢE].7>􊦿#ۙdddol26mDNNG'|6?u|RRR|f58N(--ƸqصkVNe477@bbjyUs ƎKFF.]bԐʉ'p83zhzǬQeT||?O9x ,`],Ԓ$P222ӧ7nӧh"BBBT2zU hpt:@cc#W^EpiZZGT$::dz"I:ɤIX`III]TQFO~F͓O>Ipp0[l!++C1{lZ[[Yf O?4deeQ\\Lnn.6l ''{ vk NN-(+1d:$HNN̝;&O1<쳪#ț~K"7n$''gϒECCVb̘1l߾K.}fGө-L4GOXX--|>FFQ$DEEM&ݶؽ{7ӧOW)x%zs.gggSWWǪUx饗$ m$SOqeBBBxGzTTTr%|$&&""{%::ݎ 1rH{B%J}ψ`1cƠh())a̜9f233),,d̙F^}UuΝ;3g۷og͚5*wye%K0bf$ [ϕ{I;ė$фu]߹sXz<쳼K* vKBI!?oٲV(X,0`$QYYF\t ݮf\+**X~= \ָ455~zl6AAAS^^fC1LHijjWt:V+555[orhhhd2>nGݲq[\\~k)))2c ֭[,˪_pjo&kl2{1A%2f7ٹ'`08{dYxjwءZzE+wn!"oxx9rgϞ`0vFeeQ%{ҥjtL&՘ 477ԄVtru5Fj)--UR`NRU"]~VlW>j)++Ssiiz#+Y!rohZ-ZĞ={1c-ٶm[,{!N<ɰ0|pOZ 4i*'O| MߝLUV/ravMCCRө/rݪ|])N#(v,9TNcDDDp.\a9r$&C1tPbcc9t}ZP'&<n:[,ٳU#k֬$Rl2Ο8OOJŹ߶m[wQI߬o&s;cl&33={؈hTQSLEqq DDDN0HEE'O $$#GR]] R^^ΤI9~^Oss3~p[;w.۷oW= .]%SmۙϿgΝL8eYZP􊼩c_(op t:F'ORVVFPPRXX ~z>#JKK޽&RRRHHH;wr9RSSh4޽F|EBc2x7۷/ |VUw2I9J[[)))]\+V ͛7wqx;G{jjjx꟟B/_o:wzq'%tޜzGeSo 8EllJ?ϬXKgT*IO.u{#0/+DQ+O<(ɩ#GM[[6lZۛlR*҂o2ؿRvr9t:βeصkZp8_{ŋ>}:۷oW^ah|WON|Hqq1o&ƍ_~eear8^cpFsfҺ:R3310 =׫1,n-Xdd$=LLG>$ǩ&s͖~PzDD20f A-CZ@_G+VP~"6QlJ,#WVVhquJ}츸nQS34XlOt8Jw^h:K) ]1:>כS7pC=w1U^أb9FkfvcܰiሯS! @w8hZ4 zfj?9ħOS%qCjķZ}=Dunu7 7<Γ.tEǴIф|s7wk7̐qwyf? A!x|_"ݜb`dO FW⽳+C]jӟD;G7޹Ce%C0վiy!|^;7!t*?H@J.Uǩy4a9eeʼ5Q9MQDun7O"& C$ 0 7$YH3JqOhpvsNN^eL|MaGz `_ in Python. tappy generates TAP output from your ``unittest`` test cases. You can use the TAP output files with a tool like the `Jenkins TAP plugin `_ or any other TAP consumer. tappy also provides a ``tappy`` command line tool as a TAP consumer. This tool can read TAP files and display the results like a normal Python test runner. tappy provides other TAP consumers via Python APIs for programmatic access to TAP files. For the curious: tappy sounds like "happy." Installation ------------ tappy is available for download from `PyPI `_. tappy is currently supported on Python 3.5, 3.6, 3.7, and PyPy. It is continuously tested on Linux, OS X, and Windows. .. code-block:: console $ pip install tap.py TAP version 13 brings support for YAML blocks for `YAML blocks `_ associated with test results. To work with version 13, install the optional dependencies. Learn more about YAML support in the :ref:`tap-version-13` section. .. code-block:: console $ pip install tap.py[yaml] Quickstart ---------- tappy can run like the built-in ``unittest`` discovery runner. .. code-block:: console $ python -m tap This should be enough to run a unittest-based test suite and output TAP to the console. Documentation ------------- .. toctree:: :maxdepth: 2 producers consumers highlighter contributing alternatives releases tap.py-3.0/docs/make.bat0000644000076500000240000001505312514255661015227 0ustar mattstaff00000000000000@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\tappy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\tappy.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 tap.py-3.0/docs/producers.rst0000644000076500000240000001740113575312515016361 0ustar mattstaff00000000000000TAP Producers ============= tappy integrates with ``unittest`` based test cases to produce TAP output. The producers come in three varieties: support with only the standard library, support for `nose `_, and support for `pytest `_. * ``TAPTestRunner`` - This subclass of ``unittest.TextTestRunner`` provides all the functionality of ``TextTestRunner`` and generates TAP files or streams. * tappy for **nose** - tappy provides a plugin (simply called ``TAP``) for the **nose** testing tool. * tappy for **pytest** - tappy provides a plugin called ``tap`` for the **pytest** testing tool. * tappy as the test runner - tappy can run like ``python -m unittest``. Run your test suite with ``python -m tap``. By default, the producers will create one TAP file for each ``TestCase`` executed by the test suite. The files will use the name of the test case class with a ``.tap`` extension. For example: .. code-block:: python class TestFoo(unittest.TestCase): def test_identity(self): """Test numeric equality as an example.""" self.assertTrue(1 == 1) The class will create a file named ``TestFoo.tap`` containing the following. .. code-block:: tap # TAP results for TestFoo ok 1 - Test numeric equality as an example. 1..1 The producers also have streaming modes which bypass the default runner output and write TAP to the output stream instead of files. This is useful for piping output directly to tools that read TAP natively. .. code-block:: tap $ nosetests --with-tap --tap-stream tap.tests.test_parser # TAP results for TestParser ok 1 - test_after_hash_is_not_description (tap.tests.test_parser.TestParser) ok 2 - The parser extracts a bail out line. ok 3 - The parser extracts a diagnostic line. ok 4 - The TAP spec dictates that anything less than 13 is an error. ok 5 - test_finds_description (tap.tests.test_parser.TestParser) ok 6 - The parser extracts a not ok line. ok 7 - The parser extracts a test number. ok 8 - The parser extracts an ok line. ok 9 - The parser extracts a plan line. ok 10 - The parser extracts a plan line containing a SKIP. 1..10 .. image:: images/stream.gif Examples -------- The ``TAPTestRunner`` works like the ``TextTestRunner``. To use the runner, load test cases using the ``TestLoader`` and pass the tests to the run method. The sample below is the test runner used with tappy's own tests. .. literalinclude:: ../tap/tests/run.py :lines: 3- Running tappy with **nose** is as straightforward as enabling the plugin when calling ``nosetests``. .. code-block:: console $ nosetests --with-tap ............... ---------------------------------------------------------------------- Ran 15 tests in 0.020s OK The **pytest** plugin is automatically activated for **pytest** when tappy is installed. Because it is automatically activated, **pytest** users should specify an output style. .. code-block:: console $ py.test --tap-files =========================== test session starts ============================ platform linux2 -- Python 2.7.6 -- py-1.4.30 -- pytest-2.7.2 rootdir: /home/matt/tappy, inifile: plugins: tap.py collected 94 items tests/test_adapter.py ..... tests/test_directive.py ...... tests/test_line.py ...... tests/test_loader.py ...... tests/test_main.py . tests/test_nose_plugin.py ...... tests/test_parser.py ................ tests/test_pytest_plugin.py ......... tests/test_result.py ....... tests/test_rules.py ........ tests/test_runner.py ....... tests/test_tracker.py ................. ======================== 94 passed in 0.24 seconds ========================= The configuration options for each TAP tool are listed in the following sections. TAPTestRunner ------------- You can configure the ``TAPTestRunner`` from a set of class or instance methods. * ``set_stream`` - Enable streaming mode to send TAP output directly to the output stream. Use the ``set_stream`` instance method. .. code-block:: python runner = TAPTestRunner() runner.set_stream(True) * ``set_outdir`` - The ``TAPTestRunner`` gives the user the ability to set the output directory. Use the ``set_outdir`` class method. .. code-block:: python TAPTestRunner.set_outdir('/my/output/directory') * ``set_combined`` - TAP results can be directed into a single output file. Use the ``set_combined`` class method to store the results in ``testresults.tap``. .. code-block:: python TAPTestRunner.set_combined(True) * ``set_format`` - Use the ``set_format`` class method to change the format of result lines. ``{method_name}`` and ``{short_description}`` are available options. .. code-block:: python TAPTestRunner.set_format('{method_name}: {short_description}') * ``set_header`` - Turn off or on the test case header output. The default is ``True`` (ie, the header is displayed.) Use the ``set_header`` instance method. .. code-block:: python runner = TAPTestRunner() runner.set_header(False) nose TAP Plugin --------------- .. note:: To use this plugin, install it with ``pip install nose-tap``. The **nose** TAP plugin is configured from command line flags. * ``--with-tap`` - This flag is required to enable the plugin. * ``--tap-stream`` - Enable streaming mode to send TAP output directly to the output stream. * ``--tap-combined`` - Store test results in a single output file in ``testresults.tap``. * ``--tap-outdir`` - The **nose** TAP plugin also supports an optional output directory when you don't want to store the ``.tap`` files wherever you executed ``nosetests``. Use ``--tap-outdir`` followed by a directory path to store the files in a different place. The directory will be created if it does not exist. * ``--tap-format`` - Provide a different format for the result lines. ``{method_name}`` and ``{short_description}`` are available options. For example, ``'{method_name}: {short_description}'``. pytest TAP Plugin ----------------- .. note:: To use this plugin, install it with ``pip install pytest-tap``. The **pytest** TAP plugin is configured from command line flags. Since **pytest** automatically activates the TAP plugin, the plugin does nothing by default. Users must enable a TAP output mode (via ``--tap-stream|files|combined``) or the plugin will take no action. * ``--tap-stream`` - Enable streaming mode to send TAP output directly to the output stream. * ``--tap-files`` - Store test results in individual test files. One test file is created for each test case. * ``--tap-combined`` - Store test results in a single output file in ``testresults.tap``. * ``--tap-outdir`` - The **pytest** TAP plugin also supports an optional output directory when you don't want to store the ``.tap`` files wherever you executed ``py.test``. Use ``--tap-outdir`` followed by a directory path to store the files in a different place. The directory will be created if it does not exist. Python and TAP -------------- The TAP specification is open-ended on certain topics. This section clarifies how tappy interprets these topics. The specification indicates that a test line represents a "test point" without explicitly defining "test point." tappy assumes that each test line is **per test method**. TAP producers in other languages may output test lines **per assertion**, but the unit of work in the Python ecosystem is the test method (i.e. ``unittest``, nose, and pytest all report per method by default). tappy does not permit setting the plan. Instead, the plan is a count of the number of test methods executed. Python test runners execute all test methods in a suite, regardless of any errors encountered. Thus, the test method count should be an accurate measure for the plan. tap.py-3.0/docs/releases.rst0000644000076500000240000001327513606111701016150 0ustar mattstaff00000000000000Releases ======== Version 3.0, Released January 10, 2020 -------------------------------------- * Drop support for Python 2 (it is end-of-life). * Add support for subtests. * Run a test suite with ``python -m tap``. * Discontinue use of Pipenv for managing development. Version 2.6.2, Released October 20, 2019 ---------------------------------------- * Fix bug in streaming mode that would generate tap files when the plan was already set (affected pytest). Version 2.6.1, Released September 17, 2019 ------------------------------------------ * Fix TAP version 13 support from more-itertools behavior change. Version 2.6, Released September 16, 2019 ---------------------------------------- * Add support for Python 3.7. * Drop support for Python 3.4 (it is end-of-life). Version 2.5, Released September 15, 2018 ---------------------------------------- * Add ``set_plan`` to ``Tracker`` which allows producing the ``1..N`` plan line before any tests. * Switch code style to use Black formatting. Version 2.4, Released May 29, 2018 ---------------------------------- * Add support for producing TAP version 13 output to streaming and file reports by including the ``TAP version 13`` line. Version 2.3, Released May 15, 2018 ---------------------------------- * Add optional method to install tappy for YAML support with ``pip install tap.py[yaml]``. * Make tappy version 13 compliant by adding support for parsing YAML blocks. * ``unittest.expectedFailure`` now uses a TODO directive to better align with the specification. Version 2.2, Released January 7, 2018 ------------------------------------- * Add support for Python 3.6. * Drop support for Python 3.3 (it is end-of-life). * Use Pipenv for managing development. * Switch to pytest as the development test runner. Version 2.1, Released September 23, 2016 ---------------------------------------- * Add ``Parser.parse_text`` to parse TAP provided as a string. Version 2.0, Released July 31, 2016 ----------------------------------- * Remove nose plugin. The plugin moved to the ``nose-tap`` distribution. * Remove pytest plugin. The plugin moved to the ``pytest-tap`` distribution. * Remove Pygments syntax highlighting plugin. The plugin was merged upstream directly into the Pygments project and is available without tappy. * Drop support for Python 2.6. Version 1.9, Released March 28, 2016 ------------------------------------ * ``TAPTestRunner`` has a ``set_header`` method to enable or disable test case header ouput in the TAP stream. * Add support for Python 3.5. * Perform continuous integration testing on OS X. * Drop support for Python 3.2. Version 1.8, Released November 30, 2015 --------------------------------------- * The ``tappy`` TAP consumer can read a TAP stream directly from STDIN. * Tracebacks are included as diagnostic output for failures and errors. * The ``tappy`` TAP consumer has an alternative, shorter name of ``tap``. * The pytest plugin now defaults to no output unless provided a flag. Users dependent on the old default behavior can use ``--tap-files`` to achieve the same results. * Translated into Arabic. * Translated into Chinese. * Translated into Japanese. * Translated into Russian. * Perform continuous integration testing on Windows with AppVeyor. * Improve unit test coverage to 100%. Version 1.7, Released August 19, 2015 ------------------------------------- * Provide a plugin to integrate with pytest. * Document some viable alternatives to tappy. * Translated into German. * Translated into Portuguese. Version 1.6, Released June 18, 2015 ----------------------------------- * ``TAPTestRunner`` has a ``set_stream`` method to stream all TAP output directly to an output stream instead of a file. results in a single output file. * The ``nosetests`` plugin has an optional ``--tap-stream`` flag to stream all TAP output directly to an output stream instead of a file. * tappy is now internationalized. It is translated into Dutch, French, Italian, and Spanish. * tappy is available as a Python wheel package, the new Python packaging standard. Version 1.5, Released May 18, 2015 ---------------------------------- * ``TAPTestRunner`` has a ``set_combined`` method to collect all results in a single output file. * The ``nosetests`` plugin has an optional ``--tap-combined`` flag to collect all results in a single output file. * ``TAPTestRunner`` has a ``set_format`` method to specify line format. * The ``nosetests`` plugin has an optional ``--tap-format`` flag to specify line format. Version 1.4, Released April 4, 2015 ----------------------------------- * Update ``setup.py`` to support Debian packaging. Include man page. Version 1.3, Released January 9, 2015 ------------------------------------- * The ``tappy`` command line tool is available as a TAP consumer. * The ``Parser`` and ``Loader`` are available as APIs for programmatic handling of TAP files and data. Version 1.2, Released December 21, 2014 --------------------------------------- * Provide a syntax highlighter for Pygments so any project using Pygments (e.g., Sphinx) can highlight TAP output. Version 1.1, Released October 23, 2014 -------------------------------------- * ``TAPTestRunner`` has a ``set_outdir`` method to specify where to store ``.tap`` files. * The ``nosetests`` plugin has an optional ``--tap-outdir`` flag to specify where to store ``.tap`` files. * tappy has backported support for Python 2.6. * tappy has support for Python 3.2, 3.3, and 3.4. * tappy has support for PyPy. Version 1.0, Released March 16, 2014 ------------------------------------ * Initial release of tappy * ``TAPTestRunner`` - A test runner for ``unittest`` modules that generates TAP files. * Provides a plugin for integrating with **nose**. tap.py-3.0/docs/sample_tap.txt0000644000076500000240000000074212514255661016507 0ustar mattstaff00000000000000TAP version 13 1..7 # This is a full sample TAP file. It should try all the functionality of TAP. ok 1 A passing test not ok A failing test ok 3 An unexpected success # TODO That was unexpected. not ok 4 An expected failure # TODO Because it is not done yet. ok 5 A skipped test # SKIP Because. Just because. not ok 6 A skipped test # SKIP Failing or not does not matter. Bail out! Something blew up. ok 7 This should not have happened because the test supposedly bailed out. tap.py-3.0/docs/tappy.1.rst0000644000076500000240000000255113606111604015636 0ustar mattstaff00000000000000:orphan: tappy manual page ================= Synopsis -------- **tappy** [*options*] <*pathname*> [<*pathname*> ...] Description ----------- The :program:`tappy` command consumes the list of tap files given as *pathname* s and produces an output similar to what the regular text test-runner from python's :py:mod:`unittest` module would. If *pathname* points to a directory, :program:`tappy` will look in that directory for ``*.tap`` files to consume. If you have a tool that consumes the `unittest` regular output, but wish to use the TAP protocol to better integrate with other tools, you may use tappy to *replay* tests from .tap files, without having to actually run the tests again (which is much faster). It is also an example of how to use the tap consumer API provided by the :py:mod:`tap` module. .. warning:: :program:`tappy`'s output will differ from the standard :py:mod:`unittest` output. Indeed it cannot reproduce error and failure messages (e.g. stack traces, ...) that are not recorded in tap files. Options ------- -h, --help show a short description and option list and exit. -v, --verbose produce verbose output Author ------ The :program:`tappy` and the :py:mod:`tap` modules were written by Matt LAYMAN (https://github.com/python-tap/tappy). This manual page was written Nicolas CANIART, for the Debian project. tap.py-3.0/setup.cfg0000644000076500000240000000044513606112130014474 0ustar mattstaff00000000000000[bdist_wheel] universal = 1 [compile_catalog] domain = tappy directory = tap/locale [extract_messages] input_dirs = tap output_file = tap/locale/tappy.pot copyright_holder = Matt Layman [flake8] max-line-length = 88 [metadata] license-file = LICENSE [egg_info] tag_build = tag_date = 0 tap.py-3.0/setup.py0000644000076500000240000000521213601141064014365 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors """ tappy is a set of tools for working with the `Test Anything Protocol (TAP) `_, a line based test protocol for recording test data in a standard way. Follow tappy development on `GitHub `_. Developer documentation is on `Read the Docs `_. """ from setuptools import find_packages, setup, Command import tap class ReleaseCommand(Command): description = "generate distribution release artifacts" user_options = [] def initialize_options(self): """Initialize options. This method overrides a required abstract method. """ def finalize_options(self): """Finalize options. This method overrides a required abstract method. """ def run(self): """Generate the distribution release artifacts. The custom command is used to ensure that compiling po to mo is not skipped. """ self.run_command("compile_catalog") self.run_command("sdist") self.run_command("bdist_wheel") # The docs import setup.py for the version so only call setup when not behaving # as a module. if __name__ == "__main__": with open("docs/releases.rst", "r") as f: releases = f.read() long_description = __doc__ + "\n\n" + releases setup( name="tap.py", version=tap.__version__, url="https://github.com/python-tap/tappy", license="BSD", author="Matt Layman", author_email="matthewlayman@gmail.com", description="Test Anything Protocol (TAP) tools", long_description=long_description, packages=find_packages(), entry_points={ "console_scripts": ["tappy = tap.main:main", "tap = tap.main:main"] }, include_package_data=True, zip_safe=False, platforms="any", install_requires=[], extras_require={"yaml": ["more-itertools", "PyYAML>=5.1"]}, classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Testing", ], keywords=["TAP", "unittest"], cmdclass={"release": ReleaseCommand}, ) tap.py-3.0/tap/0000755000076500000240000000000013606112130013434 5ustar mattstaff00000000000000tap.py-3.0/tap/__init__.py0000644000076500000240000000020713573576145015573 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors from .runner import TAPTestRunner __all__ = ["TAPTestRunner"] __version__ = "3.0" tap.py-3.0/tap/__main__.py0000644000076500000240000000006013575312515015540 0ustar mattstaff00000000000000from tap.main import main_module main_module() tap.py-3.0/tap/adapter.py0000644000076500000240000000307313540027652015444 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors class Adapter(object): """The adapter processes a TAP test line and updates a unittest result. It is an alternative to TestCase to collect TAP results. """ failureException = AssertionError def __init__(self, filename, line): self._filename = filename self._line = line def shortDescription(self): """Get the short description for verbeose results.""" return self._line.description def __call__(self, result): """Update test result with the lines in the TAP file. Provide the interface that TestCase provides to a suite or runner. """ result.startTest(self) if self._line.skip: result.addSkip(None, self._line.directive.reason) return if self._line.todo: if self._line.ok: result.addUnexpectedSuccess(self) else: result.addExpectedFailure(self, (Exception, Exception(), None)) return if self._line.ok: result.addSuccess(self) else: self.addFailure(result) def addFailure(self, result): """Add a failure to the result.""" result.addFailure(self, (Exception, Exception(), None)) # Since TAP will not provide assertion data, clean up the assertion # section so it is not so spaced out. test, err = result.failures[-1] result.failures[-1] = (test, "") def __repr__(self): return "".format(filename=self._filename) tap.py-3.0/tap/directive.py0000644000076500000240000000347313553150747016014 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import re class Directive(object): """A representation of a result line directive.""" skip_pattern = re.compile( r"""^SKIP\S* (?P\s*) # Optional whitespace. (?P.*) # Slurp up the rest.""", re.IGNORECASE | re.VERBOSE, ) todo_pattern = re.compile( r"""^TODO\b # The directive name (?P\s*) # Immediately following must be whitespace. (?P.*) # Slurp up the rest.""", re.IGNORECASE | re.VERBOSE, ) def __init__(self, text): r"""Initialize the directive by parsing the text. The text is assumed to be everything after a '#\s*' on a result line. """ self._text = text self._skip = False self._todo = False self._reason = None match = self.skip_pattern.match(text) if match: self._skip = True self._reason = match.group("reason") match = self.todo_pattern.match(text) if match: if match.group("whitespace"): self._todo = True else: # Catch the case where the directive has no descriptive text. if match.group("reason") == "": self._todo = True self._reason = match.group("reason") @property def text(self): """Get the entire text.""" return self._text @property def skip(self): """Check if the directive is a SKIP type.""" return self._skip @property def todo(self): """Check if the directive is a TODO type.""" return self._todo @property def reason(self): """Get the reason for the directive.""" return self._reason tap.py-3.0/tap/formatter.py0000644000076500000240000000137213422131136016017 0ustar mattstaff00000000000000import traceback def format_exception(exception): """Format an exception as diagnostics output. exception is the tuple as expected from sys.exc_info. """ exception_lines = traceback.format_exception(*exception) # The lines returned from format_exception do not strictly contain # one line per element in the list (i.e. some elements have new # line characters in the middle). Normalize that oddity. lines = "".join(exception_lines).splitlines(True) return format_as_diagnostics(lines) def format_as_diagnostics(lines): """Format the lines as diagnostics output by prepending the diagnostic #. This function makes no assumptions about the line endings. """ return "".join(["# " + line for line in lines]) tap.py-3.0/tap/i18n.py0000644000076500000240000000036613540027652014605 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import gettext import os localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "locale") translate = gettext.translation("tappy", localedir, fallback=True) _ = translate.gettext tap.py-3.0/tap/line.py0000644000076500000240000001126013601141064014740 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors try: import yaml LOAD_YAML = True except ImportError: # pragma: no cover LOAD_YAML = False class Line(object): """Base type for TAP data. TAP is a line based protocol. Thus, the most primitive type is a line. """ @property def category(self): raise NotImplementedError class Result(Line): """Information about an individual test line.""" def __init__( self, ok, number=None, description="", directive=None, diagnostics=None, raw_yaml_block=None, ): self._ok = ok if number: self._number = int(number) else: # The number may be an empty string so explicitly set to None. self._number = None self._description = description self.directive = directive self.diagnostics = diagnostics self._yaml_block = raw_yaml_block @property def category(self): """:returns: ``test``""" return "test" @property def ok(self): """Get the ok status. :rtype: bool """ return self._ok @property def number(self): """Get the test number. :rtype: int """ return self._number @property def description(self): """Get the description.""" return self._description @property def skip(self): """Check if this test was skipped. :rtype: bool """ return self.directive.skip @property def todo(self): """Check if this test was a TODO. :rtype: bool """ return self.directive.todo @property def yaml_block(self): """Lazy load a yaml_block. If yaml support is not available, there is an error in parsing the yaml block, or no yaml is associated with this result, ``None`` will be returned. :rtype: dict """ if LOAD_YAML and self._yaml_block is not None: try: yaml_dict = yaml.load(self._yaml_block, Loader=yaml.SafeLoader) return yaml_dict except yaml.error.YAMLError: print("Error parsing yaml block. Check formatting.") return None def __str__(self): is_not = "" if not self.ok: is_not = "not " directive = "" if self.directive is not None and self.directive.text: directive = " # {0}".format(self.directive.text) diagnostics = "" if self.diagnostics is not None: diagnostics = "\n" + self.diagnostics.rstrip() return "{0}ok {1} {2}{3}{4}".format( is_not, self.number, self.description, directive, diagnostics ) class Plan(Line): """A plan line to indicate how many tests to expect.""" def __init__(self, expected_tests, directive=None): self._expected_tests = expected_tests self.directive = directive @property def category(self): """:returns: ``plan``""" return "plan" @property def expected_tests(self): """Get the number of expected tests. :rtype: int """ return self._expected_tests @property def skip(self): """Check if this plan should skip the file. :rtype: bool """ return self.directive.skip class Diagnostic(Line): """A diagnostic line (i.e. anything starting with a hash).""" def __init__(self, text): self._text = text @property def category(self): """:returns: ``diagnostic``""" return "diagnostic" @property def text(self): """Get the text.""" return self._text class Bail(Line): """A bail out line (i.e. anything starting with 'Bail out!').""" def __init__(self, reason): self._reason = reason @property def category(self): """:returns: ``bail``""" return "bail" @property def reason(self): """Get the reason.""" return self._reason class Version(Line): """A version line (i.e. of the form 'TAP version 13').""" def __init__(self, version): self._version = version @property def category(self): """:returns: ``version``""" return "version" @property def version(self): """Get the version number. :rtype: int """ return self._version class Unknown(Line): """A line that represents something that is not a known TAP line. This exists for the purpose of a Null Object pattern. """ @property def category(self): """:returns: ``unknown``""" return "unknown" tap.py-3.0/tap/loader.py0000644000076500000240000000567213540027652015301 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import os import unittest from tap.adapter import Adapter from tap.parser import Parser from tap.rules import Rules class Loader(object): """Load TAP lines into unittest-able objects.""" ignored_lines = set(["diagnostic", "unknown"]) def __init__(self): self._parser = Parser() def load(self, files): """Load any files found into a suite. Any directories are walked and their files are added as TAP files. :returns: A ``unittest.TestSuite`` instance """ suite = unittest.TestSuite() for filepath in files: if os.path.isdir(filepath): self._find_tests_in_directory(filepath, suite) else: suite.addTest(self.load_suite_from_file(filepath)) return suite def load_suite_from_file(self, filename): """Load a test suite with test lines from the provided TAP file. :returns: A ``unittest.TestSuite`` instance """ suite = unittest.TestSuite() rules = Rules(filename, suite) if not os.path.exists(filename): rules.handle_file_does_not_exist() return suite line_generator = self._parser.parse_file(filename) return self._load_lines(filename, line_generator, suite, rules) def load_suite_from_stdin(self): """Load a test suite with test lines from the TAP stream on STDIN. :returns: A ``unittest.TestSuite`` instance """ suite = unittest.TestSuite() rules = Rules("stream", suite) line_generator = self._parser.parse_stdin() return self._load_lines("stream", line_generator, suite, rules) def _find_tests_in_directory(self, directory, suite): """Find test files in the directory and add them to the suite.""" for dirpath, dirnames, filenames in os.walk(directory): for filename in filenames: filepath = os.path.join(dirpath, filename) suite.addTest(self.load_suite_from_file(filepath)) def _load_lines(self, filename, line_generator, suite, rules): """Load a suite with lines produced by the line generator.""" line_counter = 0 for line in line_generator: line_counter += 1 if line.category in self.ignored_lines: continue if line.category == "test": suite.addTest(Adapter(filename, line)) rules.saw_test() elif line.category == "plan": if line.skip: rules.handle_skipping_plan(line) return suite rules.saw_plan(line, line_counter) elif line.category == "bail": rules.handle_bail(line) return suite elif line.category == "version": rules.saw_version_at(line_counter) rules.check(line_counter) return suite tap.py-3.0/tap/locale/0000755000076500000240000000000013606112130014673 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ar/0000755000076500000240000000000013606112130015275 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ar/LC_MESSAGES/0000755000076500000240000000000013606112130017062 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ar/LC_MESSAGES/tappy.mo0000644000076500000240000000517113606112130020560 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D#Y}Ges!U:E,, 50 ?f &4 [ # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: ar Language-Team: Arabic (http://www.transifex.com/python-tap/tappy/language/ar/) Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # النتائج TAP ل {test_case}(فشل متوقع)(نجاح غير متوقع) المستهلك TAP ل Pythonملف يحتوي على TAP الانتاج. سيتم فحص أي الدلائل المذكورة للملفات التي تتضمن كملفات TAP.يجب أن تظهر خطة في بداية أو نهاية الملف.سيئة سلسلة تنسيق: {format} خيارات استبدال هي: {{short_description}} و {{method_name}}إنقاذها: {reason}يتوقع {expected_count} الاختبارات ولكن فقط {seen_count} ركض.ومن خطأ لتحديد صراحة أقل أي إصدار من 13.في عداد المفقودين الخطة.ظهرت خطوط النسخة متعددة.يسمح خط خطة واحدة فقط لكل ملف.يجب أن يكون الإصدار في السطر الأول.عندما يتم إعطاء أية ملفات أو شرطة (-) يستخدم لاسم الملف، tappy سيقرأ تيار TAP من STDIN.استخدام الرسائل مطول{filename} غير موجود.tap.py-3.0/tap/locale/ar/LC_MESSAGES/tappy.po0000644000076500000240000000672212747205412020601 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Arabic (http://www.transifex.com/python-tap/tappy/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: ar\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr " المستهلك TAP ل Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "عندما يتم إعطاء أية ملفات أو شرطة (-) يستخدم لاسم الملف، tappy سيقرأ تيار TAP من STDIN." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "ملف يحتوي على TAP الانتاج. سيتم فحص أي الدلائل المذكورة للملفات التي تتضمن كملفات TAP." #: tap/main.py:43 msgid "use verbose messages" msgstr "استخدام الرسائل مطول" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "ومن خطأ لتحديد صراحة أقل أي إصدار من 13." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "ظهرت خطوط النسخة متعددة." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "يجب أن يكون الإصدار في السطر الأول." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "في عداد المفقودين الخطة." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "يسمح خط خطة واحدة فقط لكل ملف." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "يجب أن تظهر خطة في بداية أو نهاية الملف." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "يتوقع {expected_count} الاختبارات ولكن فقط {seen_count} ركض." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "إنقاذها: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} غير موجود." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(فشل متوقع)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(نجاح غير متوقع)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "سيئة سلسلة تنسيق: {format}\nخيارات استبدال هي: {{short_description}} و {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# النتائج TAP ل {test_case}" tap.py-3.0/tap/locale/de/0000755000076500000240000000000013606112130015263 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/de/LC_MESSAGES/0000755000076500000240000000000013606112130017050 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/de/LC_MESSAGES/tappy.mo0000644000076500000240000000456513606112130020554 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D!(<Rtr6^}=A"*,M-z 4 U # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: de Language-Team: German (http://www.transifex.com/python-tap/tappy/language/de/) Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # TAP Ergebnisse für {test_case}(erwartete Ausfall)(unerwarteten Erfolg)Ein TAP Verbraucher für PythonEine Datei mit TAP-Ausgang. Alle aufgeführten Verzeichnisse werden gescannt für Dateien als TAP-Dateien enthalten.Ein Plan muss am Anfang oder Ende der Datei angezeigt.Bad Format-String: {format} Austausch-Optionen sind: {{short_description}} und {{method_name}}Gerettet: {reason}Erwartete {expected_count} Tests jedoch nur {seen_count} ran.Es ist ein Fehler explizit jede Version niedriger als 13 angeben.Vermissen Sie einen Plan.Mehrere Version Linien erschienen.Nur ein Planeinzel wird pro Datei zulässig.Die Version muss auf der ersten Zeile stehen.Wenn keine Dateien angegeben oder ein Bindestrich (-) wird für den Dateinamen verwendet wird, wird tappy eine TAP Strom von STDIN gelesen.benutzen ausführliche Meldungen{filename} ist nicht vorhanden.tap.py-3.0/tap/locale/de/LC_MESSAGES/tappy.po0000644000076500000240000000631612747205412020566 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: German (http://www.transifex.com/python-tap/tappy/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Ein TAP Verbraucher für Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Wenn keine Dateien angegeben oder ein Bindestrich (-) wird für den Dateinamen verwendet wird, wird tappy eine TAP Strom von STDIN gelesen." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "Eine Datei mit TAP-Ausgang. Alle aufgeführten Verzeichnisse werden gescannt für Dateien als TAP-Dateien enthalten." #: tap/main.py:43 msgid "use verbose messages" msgstr "benutzen ausführliche Meldungen" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "Es ist ein Fehler explizit jede Version niedriger als 13 angeben." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Mehrere Version Linien erschienen." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "Die Version muss auf der ersten Zeile stehen." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Vermissen Sie einen Plan." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Nur ein Planeinzel wird pro Datei zulässig." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "Ein Plan muss am Anfang oder Ende der Datei angezeigt." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "Erwartete {expected_count} Tests jedoch nur {seen_count} ran." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Gerettet: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} ist nicht vorhanden." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(erwartete Ausfall)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(unerwarteten Erfolg)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Bad Format-String: {format}\nAustausch-Optionen sind: {{short_description}} und {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# TAP Ergebnisse für {test_case}" tap.py-3.0/tap/locale/es/0000755000076500000240000000000013606112130015302 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/es/LC_MESSAGES/0000755000076500000240000000000013606112130017067 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/es/LC_MESSAGES/tappy.mo0000644000076500000240000000462313606112130020566 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D!):Nl:g5EIB+Q5},` } # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: es Language-Team: Spanish (http://www.transifex.com/python-tap/tappy/language/es/) Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # Resultados TAP para {test_case}(fallo esperado)(éxito inesperado)Un consumidor TAP para PythonUna salida de TAP archivo que contiene. Cualquier directorios listados serán analizados en busca de archivos que incluyen como archivos TAP.Un plan debe aparecer al principio o al final del archivo.Cadena de formato Malo: {format} Las opciones de reemplazo son: {{short_description}} y {{method_name}}Rescatados: {reason}Esperados {expected_count} pruebas, pero sólo {seen_count} corrían.Es un error especificar explícitamente cualquier versión inferior a 13.Falta un plan.Múltiples líneas de versión aparecieron.Sólo una línea de plan está permitido por archivo.La versión debe estar en la primera línea.Cuando se les da ningún archivo o un guión (-) se utiliza para el nombre de archivo, tappy leerá una corriente TAP de STDIN.utilizar mensajes detallados{filename} no existe.tap.py-3.0/tap/locale/es/LC_MESSAGES/tappy.po0000644000076500000240000000635412747205412020607 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Spanish (http://www.transifex.com/python-tap/tappy/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Un consumidor TAP para Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Cuando se les da ningún archivo o un guión (-) se utiliza para el nombre de archivo, tappy leerá una corriente TAP de STDIN." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "Una salida de TAP archivo que contiene. Cualquier directorios listados serán analizados en busca de archivos que incluyen como archivos TAP." #: tap/main.py:43 msgid "use verbose messages" msgstr "utilizar mensajes detallados" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "Es un error especificar explícitamente cualquier versión inferior a 13." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Múltiples líneas de versión aparecieron." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "La versión debe estar en la primera línea." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Falta un plan." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Sólo una línea de plan está permitido por archivo." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "Un plan debe aparecer al principio o al final del archivo." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "Esperados {expected_count} pruebas, pero sólo {seen_count} corrían." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Rescatados: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} no existe." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(fallo esperado)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(éxito inesperado)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Cadena de formato Malo: {format}\nLas opciones de reemplazo son: {{short_description}} y {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# Resultados TAP para {test_case}" tap.py-3.0/tap/locale/fr/0000755000076500000240000000000013606112130015302 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/fr/LC_MESSAGES/0000755000076500000240000000000013606112130017067 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/fr/LC_MESSAGES/tappy.mo0000644000076500000240000000464113606112130020566 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D!'8"Lo;j6AKE)V2-h  # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: fr Language-Team: French (http://www.transifex.com/python-tap/tappy/language/fr/) Plural-Forms: nplurals=2; plural=(n > 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # Résultats TAP pour {test_case}(échec attendu)(succès inattendu)Un consommateur de TAP pour PythonUne sortie TAP de fichier contenant. Tous les répertoires listés seront analysés pour les fichiers à inclure en tant que fichiers TAP.Un plan doit apparaître au début ou à la fin du fichier.Bad chaîne de format: {format} Les options de remplacement sont: {{short_description}} et {{method_name}}Renflouées: {reason}{expected_count} Attendus essais mais seulement {seen_count} ran.Il ya une erreur de spécifier explicitement une version inférieure à 13.Manquer un plan.Plusieurs lignes de version sont apparus.Seule une ligne de plan est autorisé par fichier.La version doit être sur la première ligne.Si aucun fichier ne sont donnés ou un tiret (-) est utilisé pour le nom du fichier, tappy va lire un flux de TAP à partir de STDIN.utiliser des messages verbeux{filename} ne pas exister.tap.py-3.0/tap/locale/fr/LC_MESSAGES/tappy.po0000644000076500000240000000637212747205413020610 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: French (http://www.transifex.com/python-tap/tappy/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Un consommateur de TAP pour Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Si aucun fichier ne sont donnés ou un tiret (-) est utilisé pour le nom du fichier, tappy va lire un flux de TAP à partir de STDIN." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "Une sortie TAP de fichier contenant. Tous les répertoires listés seront analysés pour les fichiers à inclure en tant que fichiers TAP." #: tap/main.py:43 msgid "use verbose messages" msgstr "utiliser des messages verbeux" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "Il ya une erreur de spécifier explicitement une version inférieure à 13." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Plusieurs lignes de version sont apparus." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "La version doit être sur la première ligne." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Manquer un plan." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Seule une ligne de plan est autorisé par fichier." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "Un plan doit apparaître au début ou à la fin du fichier." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "{expected_count} Attendus essais mais seulement {seen_count} ran." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Renflouées: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} ne pas exister." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(échec attendu)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(succès inattendu)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Bad chaîne de format: {format}\nLes options de remplacement sont: {{short_description}} et {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# Résultats TAP pour {test_case}" tap.py-3.0/tap/locale/it/0000755000076500000240000000000013606112130015307 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/it/LC_MESSAGES/0000755000076500000240000000000013606112130017074 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/it/LC_MESSAGES/tappy.mo0000644000076500000240000000452613606112130020575 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D';Rpp7i6J%&+L)x| ? # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: it Language-Team: Italian (http://www.transifex.com/python-tap/tappy/language/it/) Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # Risultati TAP per {test_case}(fallimento atteso)(successo inaspettato)Un consumatore TAP per PythonUn file contenente uscita TAP. Ogni directory elencate verranno esaminati per i file da includere come file TAP.Un piano deve apparire all'inizio o alla fine del file.Stringa di formato Bad: {format} Le opzioni di sostituzione sono: {{short_description}} e {{method_name}}Bailed: {reason}Attesi test {expected_count} ma solo {seen_count} ran.E 'un errore specificare esplicitamente qualsiasi versione inferiore a 13.Manca un piano.Diverse linee di versione è apparso.Solo una riga piano è consentito per file.La versione deve essere sulla prima riga.Quando nessun file sono dati o un trattino (-) viene utilizzato per il nome del file, tappy leggerà un flusso TAP da STDIN.utilizzare messaggi dettagliati{filename} non esiste.tap.py-3.0/tap/locale/it/LC_MESSAGES/tappy.po0000644000076500000240000000625712747205413020617 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Italian (http://www.transifex.com/python-tap/tappy/language/it/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Un consumatore TAP per Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Quando nessun file sono dati o un trattino (-) viene utilizzato per il nome del file, tappy leggerà un flusso TAP da STDIN." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "Un file contenente uscita TAP. Ogni directory elencate verranno esaminati per i file da includere come file TAP." #: tap/main.py:43 msgid "use verbose messages" msgstr "utilizzare messaggi dettagliati" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "E 'un errore specificare esplicitamente qualsiasi versione inferiore a 13." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Diverse linee di versione è apparso." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "La versione deve essere sulla prima riga." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Manca un piano." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Solo una riga piano è consentito per file." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "Un piano deve apparire all'inizio o alla fine del file." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "Attesi test {expected_count} ma solo {seen_count} ran." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Bailed: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} non esiste." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(fallimento atteso)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(successo inaspettato)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Stringa di formato Bad: {format}\nLe opzioni di sostituzione sono: {{short_description}} e {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# Risultati TAP per {test_case}" tap.py-3.0/tap/locale/ja/0000755000076500000240000000000013606112130015265 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ja/LC_MESSAGES/0000755000076500000240000000000013606112130017052 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ja/LC_MESSAGES/tappy.mo0000644000076500000240000000526613606112130020555 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D.0?TmW+G$nl<H4 B} 'j # # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: ja Language-Team: Japanese (http://www.transifex.com/python-tap/tappy/language/ja/) Plural-Forms: nplurals=1; plural=0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 以下のための#1のTAP結果 {test_case}(予想失敗)(予期せぬ成功)Python用のTAP消費者含むファイルのTAP出力。ファイルは、TAPファイルとして含まれるようにするために記載されている任意のディレクトリがスキャンされます。計画では、ファイルの先頭や末尾に表示される必要があります。バートフォーマット文字列: {format} 交換オプションには、次のとおりです。{{short_description}} と {{method_name}}保釈:{reason}期待 {expected_count} テストだけ {seen_count} 走りました。これは、明示的に13以外の任意のバージョンが低い指定するとエラーになります。計画がありません。複数のバージョンのラインが登場しました。唯一の計画線は、ファイルごとに許可されています。バージョンは、最初の行になければなりません。ファイルを指定しないか、ダッシュをすると( - )のファイル名に使用され、tappyはSTDINからのTAPストリームを読み込みます。詳細メッセージを使用します{filename} は存在しません。tap.py-3.0/tap/locale/ja/LC_MESSAGES/tappy.po0000644000076500000240000000701712747205414020571 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Japanese (http://www.transifex.com/python-tap/tappy/language/ja/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Python用のTAP消費者" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "ファイルを指定しないか、ダッシュをすると( - )のファイル名に使用され、tappyはSTDINからのTAPストリームを読み込みます。" #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "含むファイルのTAP出力。ファイルは、TAPファイルとして含まれるようにするために記載されている任意のディレクトリがスキャンされます。" #: tap/main.py:43 msgid "use verbose messages" msgstr "詳細メッセージを使用します" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "これは、明示的に13以外の任意のバージョンが低い指定するとエラーになります。" #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "複数のバージョンのラインが登場しました。" #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "バージョンは、最初の行になければなりません。" #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "計画がありません。" #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "唯一の計画線は、ファイルごとに許可されています。" #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "計画では、ファイルの先頭や末尾に表示される必要があります。" #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "期待 {expected_count} テストだけ {seen_count} 走りました。" #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "保釈:{reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} は存在しません。" #: tap/runner.py:63 msgid "(expected failure)" msgstr "(予想失敗)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(予期せぬ成功)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "バートフォーマット文字列: {format}\n交換オプションには、次のとおりです。{{short_description}} と {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "以下のための#1のTAP結果 {test_case}" tap.py-3.0/tap/locale/nl/0000755000076500000240000000000013606112130015304 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/nl/LC_MESSAGES/0000755000076500000240000000000013606112130017071 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/nl/LC_MESSAGES/tappy.mo0000644000076500000240000000452213606112130020566 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D!'9M{k5\zB5!2>!q 9 # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: nl Language-Team: Dutch (http://www.transifex.com/python-tap/tappy/language/nl/) Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # TAP resultaten voor {test_case}(verwachte falen)(onverwacht succes)Een TAP consument voor PythonEen bestand met TAP output. Elke beursgenoteerde directories zal worden gescand op bestanden op te nemen als TAP bestanden.Een plan moet aan het begin of einde van het bestand.Bad format string: {format} Vervanging opties zijn: {{short_description}} en {{method_name}}Gered: {reason}Verwachte {expected_count} testen, maar slechts {seen_count} liep.Het is een fout om expliciet een versie lager dan 13.Het missen van een plan.Meerdere versie lijnen verscheen.Slechts één plan lijn is toegestaan per bestand.De versie moet op de eerste lijn.Als er geen bestanden worden gegeven of een streepje (-) wordt gebruikt voor de bestandsnaam, zal tappy een TAP stream van STDIN lezen.Gebruik uitgebreide berichten{filename} bestaat niet.tap.py-3.0/tap/locale/nl/LC_MESSAGES/tappy.po0000644000076500000240000000625312747205414020611 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Dutch (http://www.transifex.com/python-tap/tappy/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Een TAP consument voor Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Als er geen bestanden worden gegeven of een streepje (-) wordt gebruikt voor de bestandsnaam, zal tappy een TAP stream van STDIN lezen." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "Een bestand met TAP output. Elke beursgenoteerde directories zal worden gescand op bestanden op te nemen als TAP bestanden." #: tap/main.py:43 msgid "use verbose messages" msgstr "Gebruik uitgebreide berichten" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "Het is een fout om expliciet een versie lager dan 13." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Meerdere versie lijnen verscheen." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "De versie moet op de eerste lijn." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Het missen van een plan." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Slechts één plan lijn is toegestaan per bestand." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "Een plan moet aan het begin of einde van het bestand." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "Verwachte {expected_count} testen, maar slechts {seen_count} liep." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Gered: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} bestaat niet." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(verwachte falen)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(onverwacht succes)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Bad format string: {format}\nVervanging opties zijn: {{short_description}} en {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# TAP resultaten voor {test_case}" tap.py-3.0/tap/locale/pt/0000755000076500000240000000000013606112130015316 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/pt/LC_MESSAGES/0000755000076500000240000000000013606112130017103 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/pt/LC_MESSAGES/tappy.mo0000644000076500000240000000461113606112130020577 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D$ /@Us9p5BGF#Y-}'W q # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: pt Language-Team: Portuguese (http://www.transifex.com/python-tap/tappy/language/pt/) Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # Resultados da TAP para {test_case}(falha esperado)(sucesso inesperado)Um consumidor TAP para PythonA saída TAP arquivo que contém. Quaisquer diretórios listados serão verificados para arquivos a serem incluídos como arquivos TAP.Um plano deve aparecer no início ou no final do arquivo.Bad seqüência de formato: {format} As opções de substituição são: {{short_description}} e {{method_name}}Afiançado: {reason}Esperados {expected_count} testes, mas apenas {seen_count} correu.É um erro especificar explicitamente qualquer versão menor do que 13.Faltando um plano.Várias linhas de versão apareceu.Só é permitido uma linha por arquivo plano.A versão deve estar na primeira linha.Quando não há arquivos são dados ou um traço (-) é usado para o nome do arquivo, tappy vai ler um fluxo TAP a partir do STDIN.usar mensagens detalhadas{filename} não existe.tap.py-3.0/tap/locale/pt/LC_MESSAGES/tappy.po0000644000076500000240000000634212747205415020623 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Portuguese (http://www.transifex.com/python-tap/tappy/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "Um consumidor TAP para Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Quando não há arquivos são dados ou um traço (-) é usado para o nome do arquivo, tappy vai ler um fluxo TAP a partir do STDIN." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "A saída TAP arquivo que contém. Quaisquer diretórios listados serão verificados para arquivos a serem incluídos como arquivos TAP." #: tap/main.py:43 msgid "use verbose messages" msgstr "usar mensagens detalhadas" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "É um erro especificar explicitamente qualquer versão menor do que 13." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Várias linhas de versão apareceu." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "A versão deve estar na primeira linha." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Faltando um plano." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Só é permitido uma linha por arquivo plano." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "Um plano deve aparecer no início ou no final do arquivo." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "Esperados {expected_count} testes, mas apenas {seen_count} correu." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Afiançado: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} não existe." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(falha esperado)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(sucesso inesperado)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Bad seqüência de formato: {format}\nAs opções de substituição são: {{short_description}} e {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# Resultados da TAP para {test_case}" tap.py-3.0/tap/locale/ru/0000755000076500000240000000000013606112130015321 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ru/LC_MESSAGES/0000755000076500000240000000000013606112130017106 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/ru/LC_MESSAGES/tappy.mo0000644000076500000240000000571013606112130020603 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)LD-3#(@Y |d^_Y > O @i >c % # TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: ru Language-Team: Russian (http://www.transifex.com/python-tap/tappy/language/ru/) Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 # Результаты TAP для {test_case}(ожидается недостаточность)(неожиданный успех)TAP потребителя для PythonФайл, содержащий выход ТАР. Все каталоги, перечисленные будут проверяться на файлы, чтобы включить в TAP-файлов.План должен появиться в начале или в конце файла.Плохой строка формата: {format} Параметры замены: {{short_description}} и {{method_name}}Выручил: {reason}Ожидаемые {expected_count} испытания, но только {seen_count} РАН.Это ошибка явно указать любой версии ниже, чем 13 лет.Отсутствует план.Несколько строк версия появилась.Только один план линии допускается за файл.Версия должна быть на первой линии.Если файлы не указаны или тире (-) используется в качестве имени файла, tappy будет читать TAP поток со STDIN.использовать подробные сообщения{filename} не существует.tap.py-3.0/tap/locale/ru/LC_MESSAGES/tappy.po0000644000076500000240000000744112747205415020627 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Russian (http://www.transifex.com/python-tap/tappy/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "TAP потребителя для Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "Если файлы не указаны или тире (-) используется в качестве имени файла, tappy будет читать TAP поток со STDIN." #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "Файл, содержащий выход ТАР. Все каталоги, перечисленные будут проверяться на файлы, чтобы включить в TAP-файлов." #: tap/main.py:43 msgid "use verbose messages" msgstr "использовать подробные сообщения" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "Это ошибка явно указать любой версии ниже, чем 13 лет." #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "Несколько строк версия появилась." #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "Версия должна быть на первой линии." #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "Отсутствует план." #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "Только один план линии допускается за файл." #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "План должен появиться в начале или в конце файла." #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "Ожидаемые {expected_count} испытания, но только {seen_count} РАН." #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "Выручил: {reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename} не существует." #: tap/runner.py:63 msgid "(expected failure)" msgstr "(ожидается недостаточность)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(неожиданный успех)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "Плохой строка формата: {format}\nПараметры замены: {{short_description}} и {{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "# Результаты TAP для {test_case}" tap.py-3.0/tap/locale/tappy.pot0000644000076500000240000000435712771076077016612 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: tap.py 1.9\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy " "will read a TAP stream from STDIN." msgstr "" #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "" #: tap/main.py:43 msgid "use verbose messages" msgstr "" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "" #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "" #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "" #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "" #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "" #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "" #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "" #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "" #: tap/runner.py:63 msgid "(expected failure)" msgstr "" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "" tap.py-3.0/tap/locale/zh/0000755000076500000240000000000013606112130015314 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/zh/LC_MESSAGES/0000755000076500000240000000000013606112130017101 5ustar mattstaff00000000000000tap.py-3.0/tap/locale/zh/LC_MESSAGES/tappy.mo0000644000076500000240000000431413606112130020575 0ustar mattstaff00000000000000<=[ng7^=:?( 8)Y&i)D+GT`6ZG:Y2-$a@# TAP results for {test_case}(expected failure)(unexpected success)A TAP consumer for PythonA file containing TAP output. Any directories listed will be scanned for files to include as TAP files.A plan must appear at the beginning or end of the file.Bad format string: {format} Replacement options are: {{short_description}} and {{method_name}}Bailed: {reason}Expected {expected_count} tests but only {seen_count} ran.It is an error to explicitly specify any version lower than 13.Missing a plan.Multiple version lines appeared.Only one plan line is permitted per file.The version must be on the first line.When no files are given or a dash (-) is used for the file name, tappy will read a TAP stream from STDIN.use verbose messages{filename} does not exist.Project-Id-Version: tappy Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2016-06-19 10:03-0400 PO-Revision-Date: 2016-06-20 09:32+0000 Last-Translator: Matt Layman Language: zh Language-Team: Chinese (http://www.transifex.com/python-tap/tappy/language/zh/) Plural-Forms: nplurals=1; plural=0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 2.7.0 #TAP结果{test_case}(预期失败)(意想不到的成功)TAP消费者对于Python包含文件TAP。列出的所有目录将被扫描的文件,包括为TAP文件。一个计划必须出现在文件的开头或结尾。错误格式字符串:{format} 替换选项为:{{short_description}}和{{method_name}}跳伞:{reason}预计{expected_count}测试,但只有{seen_count}跑。它是明确指定比13低的任何版本错误。缺少计划。多个版本的线条出现。只有一个计划行每个文件允许的。版本必须在第一行。如果没有给定文件或破折号( - )用于文件名,tappy将读取STDIN一个TAP流。使用详细信息{filename}不存在。tap.py-3.0/tap/locale/zh/LC_MESSAGES/tappy.po0000644000076500000240000000604512747205416020622 0ustar mattstaff00000000000000# Translations template for tap.py. # Copyright (C) 2016 Matt Layman # This file is distributed under the same license as the tap.py project. # # Translators: # Matt Layman , 2015 msgid "" msgstr "" "Project-Id-Version: tappy\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2016-06-19 10:03-0400\n" "PO-Revision-Date: 2016-06-20 09:32+0000\n" "Last-Translator: Matt Layman \n" "Language-Team: Chinese (http://www.transifex.com/python-tap/tappy/language/zh/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" "Language: zh\n" "Plural-Forms: nplurals=1; plural=0;\n" #: tap/main.py:32 msgid "A TAP consumer for Python" msgstr "TAP消费者对于Python" #: tap/main.py:33 msgid "" "When no files are given or a dash (-) is used for the file name, tappy will " "read a TAP stream from STDIN." msgstr "如果没有给定文件或破折号( - )用于文件名,tappy将读取STDIN一个TAP流。" #: tap/main.py:38 msgid "" "A file containing TAP output. Any directories listed will be scanned for " "files to include as TAP files." msgstr "包含文件TAP。列出的所有目录将被扫描的文件,包括为TAP文件。" #: tap/main.py:43 msgid "use verbose messages" msgstr "使用详细信息" #: tap/parser.py:106 msgid "It is an error to explicitly specify any version lower than 13." msgstr "它是明确指定比13低的任何版本错误。" #: tap/rules.py:25 tap/tests/test_loader.py:82 msgid "Multiple version lines appeared." msgstr "多个版本的线条出现。" #: tap/rules.py:27 tap/tests/test_loader.py:100 msgid "The version must be on the first line." msgstr "版本必须在第一行。" #: tap/rules.py:32 tap/tests/test_rules.py:53 msgid "Missing a plan." msgstr "缺少计划。" #: tap/rules.py:36 tap/tests/test_rules.py:64 msgid "Only one plan line is permitted per file." msgstr "只有一个计划行每个文件允许的。" #: tap/rules.py:42 tap/tests/test_rules.py:44 msgid "A plan must appear at the beginning or end of the file." msgstr "一个计划必须出现在文件的开头或结尾。" #: tap/rules.py:46 tap/tests/test_rules.py:85 msgid "Expected {expected_count} tests but only {seen_count} ran." msgstr "预计{expected_count}测试,但只有{seen_count}跑。" #: tap/rules.py:70 tap/tests/test_rules.py:97 msgid "Bailed: {reason}" msgstr "跳伞:{reason}" #: tap/rules.py:74 tap/tests/test_loader.py:50 msgid "{filename} does not exist." msgstr "{filename}不存在。" #: tap/runner.py:63 msgid "(expected failure)" msgstr "(预期失败)" #: tap/runner.py:68 msgid "(unexpected success)" msgstr "(意想不到的成功)" #: tap/runner.py:80 msgid "" "Bad format string: {format}\n" "Replacement options are: {{short_description}} and {{method_name}}" msgstr "错误格式字符串:{format}\n替换选项为:{{short_description}}和{{method_name}}" #: tap/tests/test_tracker.py:19 tap/tracker.py:142 msgid "# TAP results for {test_case}" msgstr "#TAP结果{test_case}" tap.py-3.0/tap/main.py0000644000076500000240000000423713575312515014756 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import argparse import sys import unittest from tap.i18n import _ from tap.loader import Loader from tap.runner import TAPTestRunner def main(argv=sys.argv, stream=sys.stderr): """Entry point for ``tappy`` command.""" args = parse_args(argv) suite = build_suite(args) runner = unittest.TextTestRunner(verbosity=args.verbose, stream=stream) result = runner.run(suite) return get_status(result) def build_suite(args): """Build a test suite by loading TAP files or a TAP stream.""" loader = Loader() if len(args.files) == 0 or args.files[0] == "-": suite = loader.load_suite_from_stdin() else: suite = loader.load(args.files) return suite def parse_args(argv): description = _("A TAP consumer for Python") epilog = _( "When no files are given or a dash (-) is used for the file name, " "tappy will read a TAP stream from STDIN." ) parser = argparse.ArgumentParser(description=description, epilog=epilog) parser.add_argument( "files", metavar="FILE", nargs="*", help=_( "A file containing TAP output. Any directories listed will be " "scanned for files to include as TAP files." ), ) parser.add_argument( "-v", "--verbose", action="store_const", default=1, const=2, help=_("use verbose messages"), ) # argparse expects the executable to be removed from argv. args = parser.parse_args(argv[1:]) # When no files are provided, the user wants to use a TAP stream on STDIN. # But they probably didn't mean it if there is no pipe connected. # In that case, print the help and exit. if not args.files and sys.stdin.isatty(): sys.exit(parser.print_help()) return args def get_status(result): """Get a return status from the result.""" if result.wasSuccessful(): return 0 else: return 1 def main_module(): """Entry point for running as ``python -m tap``.""" runner = TAPTestRunner() runner.set_stream(True) unittest.main(module=None, testRunner=runner) tap.py-3.0/tap/parser.py0000644000076500000240000001546313540160023015314 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors from io import StringIO import itertools import re import sys from tap.directive import Directive from tap.i18n import _ from tap.line import Bail, Diagnostic, Plan, Result, Unknown, Version try: from more_itertools import peekable import yaml # noqa ENABLE_VERSION_13 = True except ImportError: # pragma: no cover ENABLE_VERSION_13 = False class Parser(object): """A parser for TAP files and lines.""" # ok and not ok share most of the same characteristics. result_base = r""" \s* # Optional whitespace. (?P\d*) # Optional test number. \s* # Optional whitespace. (?P[^#]*) # Optional description before #. \#? # Optional directive marker. \s* # Optional whitespace. (?P.*) # Optional directive text. """ ok = re.compile(r"^ok" + result_base, re.VERBOSE) not_ok = re.compile(r"^not\ ok" + result_base, re.VERBOSE) plan = re.compile( r""" ^1..(?P\d+) # Match the plan details. [^#]* # Consume any non-hash character to confirm only # directives appear with the plan details. \#? # Optional directive marker. \s* # Optional whitespace. (?P.*) # Optional directive text. """, re.VERBOSE, ) diagnostic = re.compile(r"^#") bail = re.compile( r""" ^Bail\ out! \s* # Optional whitespace. (?P.*) # Optional reason. """, re.VERBOSE, ) version = re.compile(r"^TAP version (?P\d+)$") yaml_block_start = re.compile(r"^(?P\s+)-") yaml_block_end = re.compile(r"^\s+\.\.\.") TAP_MINIMUM_DECLARED_VERSION = 13 def __init__(self): self._try_peeking = False def parse_file(self, filename): """Parse a TAP file to an iterable of tap.line.Line objects. This is a generator method that will yield an object for each parsed line. The file given by `filename` is assumed to exist. """ return self.parse(open(filename, "r")) def parse_stdin(self): """Parse a TAP stream from standard input. Note: this has the side effect of closing the standard input filehandle after parsing. """ return self.parse(sys.stdin) def parse_text(self, text): """Parse a string containing one or more lines of TAP output.""" return self.parse(StringIO(text)) def parse(self, fh): """Generate tap.line.Line objects, given a file-like object `fh`. `fh` may be any object that implements both the iterator and context management protocol (i.e. it can be used in both a "with" statement and a "for...in" statement.) Trailing whitespace and newline characters will be automatically stripped from the input lines. """ with fh: try: first_line = next(fh) except StopIteration: return first_parsed = self.parse_line(first_line.rstrip()) fh_new = itertools.chain([first_line], fh) if first_parsed.category == "version" and first_parsed.version >= 13: if ENABLE_VERSION_13: fh_new = peekable(itertools.chain([first_line], fh)) self._try_peeking = True else: # pragma no cover print( """ WARNING: Optional imports not found, TAP 13 output will be ignored. To parse yaml, see requirements in docs: https://tappy.readthedocs.io/en/latest/consumers.html#tap-version-13""" ) for line in fh_new: yield self.parse_line(line.rstrip(), fh_new) def parse_line(self, text, fh=None): """Parse a line into whatever TAP category it belongs.""" match = self.ok.match(text) if match: return self._parse_result(True, match, fh) match = self.not_ok.match(text) if match: return self._parse_result(False, match, fh) if self.diagnostic.match(text): return Diagnostic(text) match = self.plan.match(text) if match: return self._parse_plan(match) match = self.bail.match(text) if match: return Bail(match.group("reason")) match = self.version.match(text) if match: return self._parse_version(match) return Unknown() def _parse_plan(self, match): """Parse a matching plan line.""" expected_tests = int(match.group("expected")) directive = Directive(match.group("directive")) # Only SKIP directives are allowed in the plan. if directive.text and not directive.skip: return Unknown() return Plan(expected_tests, directive) def _parse_result(self, ok, match, fh=None): """Parse a matching result line into a result instance.""" peek_match = None try: if fh is not None and self._try_peeking: peek_match = self.yaml_block_start.match(fh.peek()) except StopIteration: pass if peek_match is None: return Result( ok, number=match.group("number"), description=match.group("description").strip(), directive=Directive(match.group("directive")), ) indent = peek_match.group("indent") concat_yaml = self._extract_yaml_block(indent, fh) return Result( ok, number=match.group("number"), description=match.group("description").strip(), directive=Directive(match.group("directive")), raw_yaml_block=concat_yaml, ) def _extract_yaml_block(self, indent, fh): """Extract a raw yaml block from a file handler""" raw_yaml = [] indent_match = re.compile(r"^{}".format(indent)) try: next(fh) while indent_match.match(fh.peek()): raw_yaml.append(next(fh).replace(indent, "", 1)) # check for the end and stop adding yaml if encountered if self.yaml_block_end.match(fh.peek()): next(fh) break except StopIteration: pass return "\n".join(raw_yaml) def _parse_version(self, match): version = int(match.group("version")) if version < self.TAP_MINIMUM_DECLARED_VERSION: raise ValueError( _("It is an error to explicitly specify any version lower than 13.") ) return Version(version) tap.py-3.0/tap/rules.py0000644000076500000240000000701613540027652015157 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors from tap.adapter import Adapter from tap.directive import Directive from tap.i18n import _ from tap.line import Result class Rules(object): def __init__(self, filename, suite): self._filename = filename self._suite = suite self._lines_seen = {"plan": [], "test": 0, "version": []} def check(self, final_line_count): """Check the status of all provided data and update the suite.""" if self._lines_seen["version"]: self._process_version_lines() self._process_plan_lines(final_line_count) def _process_version_lines(self): """Process version line rules.""" if len(self._lines_seen["version"]) > 1: self._add_error(_("Multiple version lines appeared.")) elif self._lines_seen["version"][0] != 1: self._add_error(_("The version must be on the first line.")) def _process_plan_lines(self, final_line_count): """Process plan line rules.""" if not self._lines_seen["plan"]: self._add_error(_("Missing a plan.")) return if len(self._lines_seen["plan"]) > 1: self._add_error(_("Only one plan line is permitted per file.")) return plan, at_line = self._lines_seen["plan"][0] if not self._plan_on_valid_line(at_line, final_line_count): self._add_error( _("A plan must appear at the beginning or end of the file.") ) return if plan.expected_tests != self._lines_seen["test"]: self._add_error( _("Expected {expected_count} tests but only {seen_count} ran.").format( expected_count=plan.expected_tests, seen_count=self._lines_seen["test"], ) ) def _plan_on_valid_line(self, at_line, final_line_count): """Check if a plan is on a valid line.""" # Put the common cases first. if at_line == 1 or at_line == final_line_count: return True # The plan may only appear on line 2 if the version is at line 1. after_version = ( self._lines_seen["version"] and self._lines_seen["version"][0] == 1 and at_line == 2 ) if after_version: return True return False def handle_bail(self, bail): """Handle a bail line.""" self._add_error(_("Bailed: {reason}").format(reason=bail.reason)) def handle_file_does_not_exist(self): """Handle a test file that does not exist.""" self._add_error(_("{filename} does not exist.").format(filename=self._filename)) def handle_skipping_plan(self, skip_plan): """Handle a plan that contains a SKIP directive.""" skip_line = Result(True, None, skip_plan.directive.text, Directive("SKIP")) self._suite.addTest(Adapter(self._filename, skip_line)) def saw_plan(self, plan, at_line): """Record when a plan line was seen.""" self._lines_seen["plan"].append((plan, at_line)) def saw_test(self): """Record when a test line was seen.""" self._lines_seen["test"] += 1 def saw_version_at(self, line_counter): """Record when a version line was seen.""" self._lines_seen["version"].append(line_counter) def _add_error(self, message): """Add an error test to the suite.""" error_line = Result(False, None, message, Directive("")) self._suite.addTest(Adapter(self._filename, error_line)) tap.py-3.0/tap/runner.py0000644000076500000240000001234113575301264015335 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import os from unittest import TextTestResult, TextTestRunner from unittest.runner import _WritelnDecorator import sys from tap import formatter from tap.i18n import _ from tap.tracker import Tracker class TAPTestResult(TextTestResult): FORMAT = None def __init__(self, stream, descriptions, verbosity): super(TAPTestResult, self).__init__(stream, descriptions, verbosity) def addSubTest(self, test, subtest, err): super(TAPTestResult, self).addSubTest(test, subtest, err) if err is not None: diagnostics = formatter.format_exception(err) self.tracker.add_not_ok( self._cls_name(test), self._description(subtest), diagnostics=diagnostics, ) else: self.tracker.add_ok(self._cls_name(test), self._description(subtest)) def stopTestRun(self): """Once the test run is complete, generate each of the TAP files.""" super(TAPTestResult, self).stopTestRun() self.tracker.generate_tap_reports() def addError(self, test, err): super(TAPTestResult, self).addError(test, err) diagnostics = formatter.format_exception(err) self.tracker.add_not_ok( self._cls_name(test), self._description(test), diagnostics=diagnostics ) def addFailure(self, test, err): super(TAPTestResult, self).addFailure(test, err) diagnostics = formatter.format_exception(err) self.tracker.add_not_ok( self._cls_name(test), self._description(test), diagnostics=diagnostics ) def addSuccess(self, test): super(TAPTestResult, self).addSuccess(test) self.tracker.add_ok(self._cls_name(test), self._description(test)) def addSkip(self, test, reason): super(TAPTestResult, self).addSkip(test, reason) self.tracker.add_skip(self._cls_name(test), self._description(test), reason) def addExpectedFailure(self, test, err): super(TAPTestResult, self).addExpectedFailure(test, err) diagnostics = formatter.format_exception(err) self.tracker.add_not_ok( self._cls_name(test), self._description(test), "TODO {}".format(_("(expected failure)")), diagnostics=diagnostics, ) def addUnexpectedSuccess(self, test): super(TAPTestResult, self).addUnexpectedSuccess(test) self.tracker.add_ok( self._cls_name(test), self._description(test), "TODO {}".format(_("(unexpected success)")), ) def _cls_name(self, test): return test.__class__.__name__ def _description(self, test): if self.FORMAT: try: return self.FORMAT.format( method_name=str(test), short_description=test.shortDescription() or "", ) except KeyError: sys.exit( _( "Bad format string: {format}\n" "Replacement options are: {{short_description}} and " "{{method_name}}" ).format(format=self.FORMAT) ) return test.shortDescription() or str(test) # TODO: 2016-7-30 mblayman - Since the 2.6 signature is no longer relevant, # check the possibility of removing the module level scope. # Module level state stinks, but this is the only way to keep compatibility # with Python 2.6. The best place for the tracker is as an instance variable # on the runner, but __init__ is so different that it is not easy to create # a runner that satisfies every supported Python version. _tracker = Tracker() class TAPTestRunner(TextTestRunner): """A test runner that will behave exactly like TextTestRunner and will additionally generate TAP files for each test case""" resultclass = TAPTestResult def set_stream(self, streaming): """Set the streaming boolean option to stream TAP directly to stdout. The test runner default output will be suppressed in favor of TAP. """ self.stream = _WritelnDecorator(open(os.devnull, "w")) _tracker.streaming = streaming _tracker.stream = sys.stdout def _makeResult(self): result = self.resultclass(self.stream, self.descriptions, self.verbosity) result.tracker = _tracker return result @classmethod def set_outdir(cls, outdir): """Set the output directory so that TAP files are written to the specified outdir location. """ # Blame the lack of unittest extensibility for this hacky method. _tracker.outdir = outdir @classmethod def set_combined(cls, combined): """Set the tracker to use a single output file.""" _tracker.combined = combined @classmethod def set_header(cls, header): """Set the header display flag.""" _tracker.header = header @classmethod def set_format(cls, fmt): """Set the format of each test line. The format string can use: * {method_name}: The test method name * {short_description}: The test's docstring short description """ TAPTestResult.FORMAT = fmt tap.py-3.0/tap/tests/0000755000076500000240000000000013606112130014576 5ustar mattstaff00000000000000tap.py-3.0/tap/tests/__init__.py0000644000076500000240000000017213540027652016722 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors """Tests for tappy""" from tap.tests.testcase import TestCase # NOQA tap.py-3.0/tap/tests/factory.py0000644000076500000240000000214313540027652016632 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import sys import tempfile from unittest.runner import TextTestResult from tap.directive import Directive from tap.line import Bail, Plan, Result class Factory(object): """A factory to produce commonly needed objects""" def make_ok(self, directive_text=""): return Result(True, 1, "This is a description.", Directive(directive_text)) def make_not_ok(self, directive_text=""): return Result(False, 1, "This is a description.", Directive(directive_text)) def make_bail(self, reason="Because it is busted."): return Bail(reason) def make_plan(self, expected_tests=99, directive_text=""): return Plan(expected_tests, Directive(directive_text)) def make_test_result(self): stream = tempfile.TemporaryFile(mode="w") return TextTestResult(stream, None, 1) def make_exc(self): """Make a traceback tuple. Doing this intentionally is not straight forward. """ try: raise ValueError("boom") except ValueError: return sys.exc_info() tap.py-3.0/tap/tests/run.py0000644000076500000240000000077413540160023015765 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import os import sys import unittest from tap import TAPTestRunner if __name__ == "__main__": tests_dir = os.path.dirname(os.path.abspath(__file__)) loader = unittest.TestLoader() tests = loader.discover(tests_dir) runner = TAPTestRunner() runner.set_outdir("testout") runner.set_format("Hi: {method_name} - {short_description}") result = runner.run(tests) status = 0 if result.wasSuccessful() else 1 sys.exit(status) tap.py-3.0/tap/tests/test_adapter.py0000644000076500000240000000361413575313252017650 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors from unittest import mock from tap.adapter import Adapter from tap.tests import TestCase class TestAdapter(TestCase): """Tests for tap.adapter.Adapter""" def test_adapter_has_filename(self): """The adapter has a TAP filename.""" tap_filename = "fake.tap" adapter = Adapter(tap_filename, None) self.assertEqual(tap_filename, adapter._filename) def test_handles_ok_test_line(self): """Add a success for an ok test line.""" ok_line = self.factory.make_ok() adapter = Adapter("fake.tap", ok_line) result = mock.Mock() adapter(result) self.assertTrue(result.addSuccess.called) def test_handles_skip_test_line(self): """Add a skip when a test line contains a skip directive.""" skip_line = self.factory.make_ok(directive_text="SKIP This is the reason.") adapter = Adapter("fake.tap", skip_line) result = self.factory.make_test_result() adapter(result) self.assertEqual(1, len(result.skipped)) self.assertEqual("This is the reason.", result.skipped[0][1]) def test_handles_ok_todo_test_line(self): """Add an unexpected success for an ok todo test line.""" todo_line = self.factory.make_ok(directive_text="TODO An incomplete test") adapter = Adapter("fake.tap", todo_line) result = self.factory.make_test_result() adapter(result) self.assertEqual(1, len(result.unexpectedSuccesses)) def test_handles_not_ok_todo_test_line(self): """Add an expected failure for a not ok todo test line.""" todo_line = self.factory.make_not_ok(directive_text="TODO An incomplete test") adapter = Adapter("fake.tap", todo_line) result = self.factory.make_test_result() adapter(result) self.assertEqual(1, len(result.expectedFailures)) tap.py-3.0/tap/tests/test_directive.py0000644000076500000240000000237513540027652020207 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import unittest from tap.directive import Directive class TestDirective(unittest.TestCase): """Tests for tap.directive.Directive""" def test_finds_todo(self): text = "ToDo This is something to do." directive = Directive(text) self.assertTrue(directive.todo) def test_finds_simplest_todo(self): text = "TODO" directive = Directive(text) self.assertTrue(directive.todo) def test_todo_has_boundary(self): """TAP spec indicates TODO directives must be on a boundary.""" text = "TODO: Not a TODO directive because of an immediate colon." directive = Directive(text) self.assertFalse(directive.todo) def test_finds_skip(self): text = "Skipping This is something to skip." directive = Directive(text) self.assertTrue(directive.skip) def test_finds_simplest_skip(self): text = "SKIP" directive = Directive(text) self.assertTrue(directive.skip) def test_skip_at_beginning(self): """Only match SKIP directives at the beginning.""" text = "This is not something to skip." directive = Directive(text) self.assertFalse(directive.skip) tap.py-3.0/tap/tests/test_formatter.py0000644000076500000240000000113113422131136020211 0ustar mattstaff00000000000000from tap.formatter import format_as_diagnostics, format_exception from tap.tests import TestCase class TestFormatter(TestCase): def test_formats_as_diagnostics(self): data = ["foo\n", "bar\n"] expected_diagnostics = "# foo\n# bar\n" diagnostics = format_as_diagnostics(data) self.assertEqual(expected_diagnostics, diagnostics) def test_format_exception_as_diagnostics(self): exc = self.factory.make_exc() diagnostics = format_exception(exc) self.assertTrue(diagnostics.startswith("# ")) self.assertTrue("boom" in diagnostics) tap.py-3.0/tap/tests/test_line.py0000644000076500000240000000240513575306731017160 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import unittest from tap.directive import Directive from tap.line import Line, Result class TestLine(unittest.TestCase): """Tests for tap.line.Line""" def test_line_requires_category(self): line = Line() with self.assertRaises(NotImplementedError): line.category class TestResult(unittest.TestCase): """Tests for tap.line.Result""" def test_category(self): result = Result(True) self.assertEqual("test", result.category) def test_ok(self): result = Result(True) self.assertTrue(result.ok) def test_str_ok(self): result = Result(True, 42, "passing") self.assertEqual("ok 42 passing", str(result)) def test_str_not_ok(self): result = Result(False, 43, "failing") self.assertEqual("not ok 43 failing", str(result)) def test_str_directive(self): directive = Directive("SKIP a reason") result = Result(True, 44, "passing", directive) self.assertEqual("ok 44 passing # SKIP a reason", str(result)) def test_str_diagnostics(self): result = Result(False, 43, "failing", diagnostics="# more info") self.assertEqual("not ok 43 failing\n# more info", str(result)) tap.py-3.0/tap/tests/test_loader.py0000644000076500000240000000736613575303353017507 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import inspect from io import StringIO import os import tempfile import unittest from unittest import mock from tap.i18n import _ from tap.loader import Loader from tap.tests import TestCase class TestLoader(TestCase): """Tests for tap.loader.Loader""" def test_handles_file(self): """The loader handles a file.""" sample = inspect.cleandoc( """TAP version 13 1..2 # This is a diagnostic. ok 1 A passing test not ok 2 A failing test This is an unknown line. Bail out! This test would abort. """ ) temp = tempfile.NamedTemporaryFile(delete=False) temp.write(sample.encode("utf-8")) temp.close() loader = Loader() suite = loader.load_suite_from_file(temp.name) # The bail line counts as a failed test. self.assertEqual(3, len(suite._tests)) def test_file_does_not_exist(self): """The loader records a failure when a file does not exist.""" loader = Loader() suite = loader.load_suite_from_file("phony.tap") self.assertEqual(1, len(suite._tests)) self.assertEqual( _("{filename} does not exist.").format(filename="phony.tap"), suite._tests[0]._line.description, ) def test_handles_directory(self): directory = tempfile.mkdtemp() sub_directory = os.path.join(directory, "sub") os.mkdir(sub_directory) with open(os.path.join(directory, "a_file.tap"), "w") as f: f.write("ok A passing test") with open(os.path.join(sub_directory, "another_file.tap"), "w") as f: f.write("not ok A failing test") loader = Loader() suite = loader.load([directory]) self.assertEqual(2, len(suite._tests)) def test_errors_with_multiple_version_lines(self): sample = inspect.cleandoc( """TAP version 13 TAP version 13 1..0 """ ) temp = tempfile.NamedTemporaryFile(delete=False) temp.write(sample.encode("utf-8")) temp.close() loader = Loader() suite = loader.load_suite_from_file(temp.name) self.assertEqual(1, len(suite._tests)) self.assertEqual( _("Multiple version lines appeared."), suite._tests[0]._line.description ) def test_errors_with_version_not_on_first_line(self): sample = inspect.cleandoc( """# Something that doesn't belong. TAP version 13 1..0 """ ) temp = tempfile.NamedTemporaryFile(delete=False) temp.write(sample.encode("utf-8")) temp.close() loader = Loader() suite = loader.load_suite_from_file(temp.name) self.assertEqual(1, len(suite._tests)) self.assertEqual( _("The version must be on the first line."), suite._tests[0]._line.description, ) def test_skip_plan_aborts_loading(self): sample = inspect.cleandoc( """1..0 # Skipping this test file. ok This should not get processed. """ ) temp = tempfile.NamedTemporaryFile(delete=False) temp.write(sample.encode("utf-8")) temp.close() loader = Loader() suite = loader.load_suite_from_file(temp.name) self.assertEqual(1, len(suite._tests)) self.assertEqual("Skipping this test file.", suite._tests[0]._line.description) @mock.patch("tap.parser.sys.stdin", StringIO(u"")) def test_loads_from_stream(self): loader = Loader() suite = loader.load_suite_from_stdin() self.assertTrue(isinstance(suite, unittest.TestSuite)) tap.py-3.0/tap/tests/test_main.py0000644000076500000240000000402013575312515017145 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import argparse import os from unittest import mock from tap.loader import Loader from tap.main import build_suite, get_status, main, main_module, parse_args from tap.tests import TestCase class TestMain(TestCase): """Tests for tap.main""" def test_exits_with_error(self): """The main function returns an error status if there were failures.""" argv = ["/bin/fake", "fake.tap"] stream = open(os.devnull, "w") status = main(argv, stream=stream) self.assertEqual(1, status) def test_get_successful_status(self): result = mock.Mock() result.wasSuccessful.return_value = True self.assertEqual(0, get_status(result)) @mock.patch.object(Loader, "load_suite_from_stdin") def test_build_suite_from_stdin(self, load_suite_from_stdin): args = mock.Mock() args.files = [] expected_suite = mock.Mock() load_suite_from_stdin.return_value = expected_suite suite = build_suite(args) self.assertEqual(expected_suite, suite) @mock.patch.object(Loader, "load_suite_from_stdin") def test_build_suite_from_stdin_dash(self, load_suite_from_stdin): argv = ["/bin/fake", "-"] args = parse_args(argv) expected_suite = mock.Mock() load_suite_from_stdin.return_value = expected_suite suite = build_suite(args) self.assertEqual(expected_suite, suite) @mock.patch("tap.main.sys.stdin") @mock.patch("tap.main.sys.exit") @mock.patch.object(argparse.ArgumentParser, "print_help") def test_when_no_pipe_to_stdin(self, print_help, sys_exit, mock_stdin): argv = ["/bin/fake"] mock_stdin.isatty = mock.Mock(return_value=True) parse_args(argv) self.assertTrue(print_help.called) self.assertTrue(sys_exit.called) class TestMainModule(TestCase): @mock.patch("tap.main.unittest") def test_main_set_to_stream(self, mock_unittest): main_module() mock_unittest.main.called tap.py-3.0/tap/tests/test_parser.py0000644000076500000240000003316113575303353017525 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors from contextlib import contextmanager import inspect from io import BytesIO, StringIO import sys import tempfile import unittest from unittest import mock from tap.parser import Parser @contextmanager def captured_output(): if sys.version_info[0] < 3: new_out, new_err = BytesIO(), BytesIO() else: new_out, new_err = StringIO(), StringIO() old_out, old_err = sys.stdout, sys.stderr try: sys.stdout, sys.stderr = new_out, new_err yield sys.stdout, sys.stderr finally: sys.stdout, sys.stderr = old_out, old_err class TestParser(unittest.TestCase): """Tests for tap.parser.Parser""" def test_finds_ok(self): """The parser extracts an ok line.""" parser = Parser() line = parser.parse_line("ok - This is a passing test line.") self.assertEqual("test", line.category) self.assertTrue(line.ok) self.assertTrue(line.number is None) def test_finds_number(self): """The parser extracts a test number.""" parser = Parser() line = parser.parse_line("ok 42 is the magic number.") self.assertEqual("test", line.category) self.assertEqual(42, line.number) def test_finds_description(self): parser = Parser() line = parser.parse_line("ok 42 A passing test.") self.assertEqual("test", line.category) self.assertEqual("A passing test.", line.description) def test_after_hash_is_not_description(self): parser = Parser() line = parser.parse_line("ok A description # Not part of description.") self.assertEqual("test", line.category) self.assertEqual("A description", line.description) def test_finds_todo(self): parser = Parser() line = parser.parse_line("ok A description # TODO Not done") self.assertEqual("test", line.category) self.assertTrue(line.todo) def test_finds_skip(self): parser = Parser() line = parser.parse_line("ok A description # SKIP for now") self.assertEqual("test", line.category) self.assertTrue(line.skip) def test_finds_not_ok(self): """The parser extracts a not ok line.""" parser = Parser() line = parser.parse_line("not ok - This is a failing test line.") self.assertEqual("test", line.category) self.assertFalse(line.ok) self.assertTrue(line.number is None) self.assertEqual("", line.directive.text) def test_finds_directive(self): """The parser extracts a directive""" parser = Parser() test_line = "not ok - This line fails # TODO not implemented" line = parser.parse_line(test_line) directive = line.directive self.assertEqual("test", line.category) self.assertEqual("TODO not implemented", directive.text) self.assertFalse(directive.skip) self.assertTrue(directive.todo) self.assertEqual("not implemented", directive.reason) def test_unrecognizable_line(self): """The parser returns an unrecognizable line.""" parser = Parser() line = parser.parse_line("This is not a valid TAP line. # srsly") self.assertEqual("unknown", line.category) def test_diagnostic_line(self): """The parser extracts a diagnostic line.""" text = "# An example diagnostic line" parser = Parser() line = parser.parse_line(text) self.assertEqual("diagnostic", line.category) self.assertEqual(text, line.text) def test_bail_out_line(self): """The parser extracts a bail out line.""" parser = Parser() line = parser.parse_line("Bail out! This is the reason to bail.") self.assertEqual("bail", line.category) self.assertEqual("This is the reason to bail.", line.reason) def test_finds_version(self): """The parser extracts a version line.""" parser = Parser() line = parser.parse_line("TAP version 13") self.assertEqual("version", line.category) self.assertEqual(13, line.version) def test_errors_on_old_version(self): """The TAP spec dictates that anything less than 13 is an error.""" parser = Parser() with self.assertRaises(ValueError): parser.parse_line("TAP version 12") def test_finds_plan(self): """The parser extracts a plan line.""" parser = Parser() line = parser.parse_line("1..42") self.assertEqual("plan", line.category) self.assertEqual(42, line.expected_tests) def test_finds_plan_with_skip(self): """The parser extracts a plan line containing a SKIP.""" parser = Parser() line = parser.parse_line("1..42 # Skipping this test file.") self.assertEqual("plan", line.category) self.assertTrue(line.skip) def test_ignores_plan_with_any_non_skip_directive(self): """The parser only recognizes SKIP directives in plans.""" parser = Parser() line = parser.parse_line("1..42 # TODO will not work.") self.assertEqual("unknown", line.category) def test_parses_text(self): sample = inspect.cleandoc( u"""1..2 ok 1 A passing test not ok 2 A failing test""" ) parser = Parser() lines = [] for line in parser.parse_text(sample): lines.append(line) self.assertEqual(3, len(lines)) self.assertEqual("plan", lines[0].category) self.assertEqual("test", lines[1].category) self.assertTrue(lines[1].ok) self.assertEqual("test", lines[2].category) self.assertFalse(lines[2].ok) def test_parses_file(self): sample = inspect.cleandoc( """1..2 ok 1 A passing test not ok 2 A failing test""" ) temp = tempfile.NamedTemporaryFile(delete=False) temp.write(sample.encode("utf-8")) temp.close() parser = Parser() lines = [] for line in parser.parse_file(temp.name): lines.append(line) self.assertEqual(3, len(lines)) self.assertEqual("plan", lines[0].category) self.assertEqual("test", lines[1].category) self.assertTrue(lines[1].ok) self.assertIsNone(lines[1].yaml_block) self.assertEqual("test", lines[2].category) self.assertFalse(lines[2].ok) def test_parses_yaml(self): sample = inspect.cleandoc( u"""TAP version 13 1..2 ok 1 A passing test --- test: sample yaml ... not ok 2 A failing test""" ) parser = Parser() lines = [] for line in parser.parse_text(sample): lines.append(line) try: import yaml from more_itertools import peekable # noqa converted_yaml = yaml.safe_load(u"""test: sample yaml""") self.assertEqual(4, len(lines)) self.assertEqual(13, lines[0].version) self.assertEqual(converted_yaml, lines[2].yaml_block) self.assertEqual("test", lines[3].category) self.assertIsNone(lines[3].yaml_block) except ImportError: self.assertEqual(7, len(lines)) self.assertEqual(13, lines[0].version) for l in list(range(3, 6)): self.assertEqual("unknown", lines[l].category) self.assertEqual("test", lines[6].category) def test_parses_yaml_no_end(self): sample = inspect.cleandoc( u"""TAP version 13 1..2 ok 1 A passing test --- test: sample yaml not ok 2 A failing test""" ) parser = Parser() lines = [] for line in parser.parse_text(sample): lines.append(line) try: import yaml from more_itertools import peekable # noqa converted_yaml = yaml.safe_load(u"""test: sample yaml""") self.assertEqual(4, len(lines)) self.assertEqual(13, lines[0].version) self.assertEqual(converted_yaml, lines[2].yaml_block) self.assertEqual("test", lines[3].category) self.assertIsNone(lines[3].yaml_block) except ImportError: self.assertEqual(6, len(lines)) self.assertEqual(13, lines[0].version) for l in list(range(3, 5)): self.assertEqual("unknown", lines[l].category) self.assertEqual("test", lines[5].category) def test_parses_yaml_more_complex(self): sample = inspect.cleandoc( u"""TAP version 13 1..2 ok 1 A passing test --- message: test severity: fail data: got: - foo expect: - bar""" ) parser = Parser() lines = [] for line in parser.parse_text(sample): lines.append(line) try: import yaml from more_itertools import peekable # noqa converted_yaml = yaml.safe_load( u""" message: test severity: fail data: got: - foo expect: - bar""" ) self.assertEqual(3, len(lines)) self.assertEqual(13, lines[0].version) self.assertEqual(converted_yaml, lines[2].yaml_block) except ImportError: self.assertEqual(11, len(lines)) self.assertEqual(13, lines[0].version) for l in list(range(3, 11)): self.assertEqual("unknown", lines[l].category) def test_parses_yaml_no_association(self): sample = inspect.cleandoc( u"""TAP version 13 1..2 ok 1 A passing test # Diagnostic line --- test: sample yaml ... not ok 2 A failing test""" ) parser = Parser() lines = [] for line in parser.parse_text(sample): lines.append(line) self.assertEqual(8, len(lines)) self.assertEqual(13, lines[0].version) self.assertIsNone(lines[2].yaml_block) self.assertEqual("diagnostic", lines[3].category) for l in list(range(4, 7)): self.assertEqual("unknown", lines[l].category) self.assertEqual("test", lines[7].category) def test_parses_yaml_no_start(self): sample = inspect.cleandoc( u"""TAP version 13 1..2 ok 1 A passing test test: sample yaml ... not ok 2 A failing test""" ) parser = Parser() lines = [] for line in parser.parse_text(sample): lines.append(line) self.assertEqual(6, len(lines)) self.assertEqual(13, lines[0].version) self.assertIsNone(lines[2].yaml_block) for l in list(range(3, 5)): self.assertEqual("unknown", lines[l].category) self.assertEqual("test", lines[5].category) def test_malformed_yaml(self): self.maxDiff = None sample = inspect.cleandoc( u"""TAP version 13 1..2 ok 1 A passing test --- test: sample yaml \tfail: tabs are not allowed! ... not ok 2 A failing test""" ) yaml_err = inspect.cleandoc( u""" WARNING: Optional imports not found, TAP 13 output will be ignored. To parse yaml, see requirements in docs: https://tappy.readthedocs.io/en/latest/consumers.html#tap-version-13""" ) parser = Parser() lines = [] with captured_output() as (parse_out, _): for line in parser.parse_text(sample): lines.append(line) try: import yaml # noqa from more_itertools import peekable # noqa self.assertEqual(4, len(lines)) self.assertEqual(13, lines[0].version) with captured_output() as (out, _): self.assertIsNone(lines[2].yaml_block) self.assertEqual( "Error parsing yaml block. Check formatting.", out.getvalue().strip() ) self.assertEqual("test", lines[3].category) self.assertIsNone(lines[3].yaml_block) except ImportError: self.assertEqual(8, len(lines)) self.assertEqual(13, lines[0].version) for l in list(range(3, 7)): self.assertEqual("unknown", lines[l].category) self.assertEqual("test", lines[7].category) self.assertEqual(yaml_err, parse_out.getvalue().strip()) def test_parse_empty_file(self): temp = tempfile.NamedTemporaryFile(delete=False) temp.close() parser = Parser() lines = [] for line in parser.parse_file(temp.name): lines.append(line) self.assertEqual(0, len(lines)) @mock.patch( "tap.parser.sys.stdin", StringIO( u"""1..2 ok 1 A passing test not ok 2 A failing test""" ), ) def test_parses_stdin(self): parser = Parser() lines = [] for line in parser.parse_stdin(): lines.append(line) self.assertEqual(3, len(lines)) self.assertEqual("plan", lines[0].category) self.assertEqual("test", lines[1].category) self.assertTrue(lines[1].ok) self.assertEqual("test", lines[2].category) self.assertFalse(lines[2].ok) tap.py-3.0/tap/tests/test_result.py0000644000076500000240000000677513575301264017561 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import contextlib import os import unittest import unittest.case from tap.i18n import _ from tap.runner import TAPTestResult from tap.tests import TestCase from tap.tracker import Tracker class FakeTestCase(unittest.TestCase): def runTest(self): pass @contextlib.contextmanager def subTest(self, *args, **kwargs): try: self._subtest = unittest.case._SubTest(self, object(), {}) yield finally: self._subtest = None def __call__(self, result): pass class TestTAPTestResult(TestCase): @classmethod def _make_one(cls): # Yep, the stream is not being closed. stream = open(os.devnull, "w") result = TAPTestResult(stream, False, 0) result.tracker = Tracker() return result def test_adds_error(self): result = self._make_one() # Python 3 does some extra testing in unittest on exceptions so fake # the cause as if it were raised. ex = Exception() ex.__cause__ = None result.addError(FakeTestCase(), (None, ex, None)) self.assertEqual(len(result.tracker._test_cases["FakeTestCase"]), 1) def test_adds_failure(self): result = self._make_one() # Python 3 does some extra testing in unittest on exceptions so fake # the cause as if it were raised. ex = Exception() ex.__cause__ = None result.addFailure(FakeTestCase(), (None, ex, None)) self.assertEqual(len(result.tracker._test_cases["FakeTestCase"]), 1) def test_adds_success(self): result = self._make_one() result.addSuccess(FakeTestCase()) self.assertEqual(len(result.tracker._test_cases["FakeTestCase"]), 1) def test_adds_skip(self): result = self._make_one() result.addSkip(FakeTestCase(), "a reason") self.assertEqual(len(result.tracker._test_cases["FakeTestCase"]), 1) def test_adds_expected_failure(self): exc = self.factory.make_exc() result = self._make_one() result.addExpectedFailure(FakeTestCase(), exc) line = result.tracker._test_cases["FakeTestCase"][0] self.assertFalse(line.ok) self.assertEqual(line.directive.text, "TODO {}".format(_("(expected failure)"))) def test_adds_unexpected_success(self): result = self._make_one() result.addUnexpectedSuccess(FakeTestCase()) line = result.tracker._test_cases["FakeTestCase"][0] self.assertTrue(line.ok) self.assertEqual( line.directive.text, "TODO {}".format(_("(unexpected success)")) ) def test_adds_subtest_success(self): """Test that the runner handles subtest success results.""" result = self._make_one() test = FakeTestCase() with test.subTest(): result.addSubTest(test, test._subtest, None) line = result.tracker._test_cases["FakeTestCase"][0] self.assertTrue(line.ok) def test_adds_subtest_failure(self): """Test that the runner handles subtest failure results.""" result = self._make_one() # Python 3 does some extra testing in unittest on exceptions so fake # the cause as if it were raised. ex = Exception() ex.__cause__ = None test = FakeTestCase() with test.subTest(): result.addSubTest(test, test._subtest, (ex.__class__, ex, None)) line = result.tracker._test_cases["FakeTestCase"][0] self.assertFalse(line.ok) tap.py-3.0/tap/tests/test_rules.py0000644000076500000240000000562513540027652017364 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import unittest from tap.i18n import _ from tap.rules import Rules from tap.tests import TestCase class TestRules(TestCase): """Tests for tap.rules.Rules""" def _make_one(self): self.suite = unittest.TestSuite() return Rules("foobar.tap", self.suite) def test_handles_skipping_plan(self): skip_plan = self.factory.make_plan(directive_text="Skip on Mondays.") rules = self._make_one() rules.handle_skipping_plan(skip_plan) self.assertEqual(1, len(self.suite._tests)) self.assertEqual("Skip on Mondays.", self.suite._tests[0]._line.description) def test_tracks_plan_line(self): plan = self.factory.make_plan() rules = self._make_one() rules.saw_plan(plan, 28) self.assertEqual(rules._lines_seen["plan"][0][0], plan) self.assertEqual(rules._lines_seen["plan"][0][1], 28) def test_errors_plan_not_at_end(self): plan = self.factory.make_plan() rules = self._make_one() rules.saw_plan(plan, 41) rules.check(42) self.assertEqual( _("A plan must appear at the beginning or end of the file."), self.suite._tests[0]._line.description, ) def test_requires_plan(self): rules = self._make_one() rules.check(42) self.assertEqual(_("Missing a plan."), self.suite._tests[0]._line.description) def test_only_one_plan(self): plan = self.factory.make_plan() rules = self._make_one() rules.saw_plan(plan, 41) rules.saw_plan(plan, 42) rules.check(42) self.assertEqual( _("Only one plan line is permitted per file."), self.suite._tests[0]._line.description, ) def test_plan_line_two(self): """A plan may appear on line 2 when line 1 is a version line.""" rules = self._make_one() rules.saw_version_at(1) valid = rules._plan_on_valid_line(at_line=2, final_line_count=42) self.assertTrue(valid) def test_errors_when_expected_tests_differs_from_actual(self): plan = self.factory.make_plan(expected_tests=42) rules = self._make_one() rules.saw_plan(plan, 1) rules.saw_test() rules.check(2) self.assertEqual( _("Expected {expected_count} tests but only {seen_count} ran.").format( expected_count=42, seen_count=1 ), self.suite._tests[0]._line.description, ) def test_errors_on_bail(self): bail = self.factory.make_bail(reason="Missing something important.") rules = self._make_one() rules.handle_bail(bail) self.assertEqual(1, len(self.suite._tests)) self.assertEqual( _("Bailed: {reason}").format(reason="Missing something important."), self.suite._tests[0]._line.description, ) tap.py-3.0/tap/tests/test_runner.py0000644000076500000240000000657013575303353017546 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import os import sys import tempfile import unittest from unittest import mock from tap import TAPTestRunner from tap.runner import TAPTestResult, _tracker class TestTAPTestRunner(unittest.TestCase): def test_has_tap_test_result(self): runner = TAPTestRunner() self.assertEqual(runner.resultclass, TAPTestResult) def test_runner_uses_outdir(self): """Test that the test runner sets the outdir so that TAP files will be written to that location. Setting class attributes to get the right behavior is a dirty hack, but the unittest classes aren't very extensible. """ # Save the previous outdir in case **this** execution was using it. previous_outdir = _tracker.outdir outdir = tempfile.mkdtemp() TAPTestRunner.set_outdir(outdir) self.assertEqual(outdir, _tracker.outdir) _tracker.outdir = previous_outdir def test_runner_uses_format(self): """Test that format is set on TAPTestResult FORMAT.""" # Save the previous format in case **this** execution was using it. previous_format = TAPTestResult.FORMAT fmt = "{method_name}: {short_description}" TAPTestRunner.set_format(fmt) self.assertEqual(fmt, TAPTestResult.FORMAT) TAPTestResult.FORMAT = previous_format def test_runner_uses_combined(self): """Test that output is combined.""" # Save previous combined in case **this** execution was using it. previous_combined = _tracker.combined TAPTestRunner.set_combined(True) self.assertTrue(_tracker.combined) _tracker.combined = previous_combined @mock.patch("sys.exit") def test_bad_format_string(self, fake_exit): """A bad format string exits the runner.""" previous_format = TAPTestResult.FORMAT bad_format = "Not gonna work {sort_desc}" TAPTestRunner.set_format(bad_format) result = TAPTestResult(None, True, 1) test = mock.Mock() result._description(test) self.assertTrue(fake_exit.called) TAPTestResult.FORMAT = previous_format def test_runner_sets_tracker_for_streaming(self): """The tracker is set for streaming mode.""" previous_streaming = _tracker.streaming previous_stream = _tracker.stream runner = TAPTestRunner() runner.set_stream(True) self.assertTrue(_tracker.streaming) self.assertTrue(_tracker.stream, sys.stdout) _tracker.streaming = previous_streaming _tracker.stream = previous_stream def test_runner_stream_to_devnull_for_streaming(self): previous_streaming = _tracker.streaming previous_stream = _tracker.stream runner = TAPTestRunner() runner.set_stream(True) self.assertTrue(runner.stream.stream.name, os.devnull) _tracker.streaming = previous_streaming _tracker.stream = previous_stream def test_runner_uses_header(self): """Test that the case header can be turned off.""" # Save previous header in case **this** execution was using it. previous_header = _tracker.header TAPTestRunner.set_header(False) self.assertFalse(_tracker.header) TAPTestRunner.set_header(True) self.assertTrue(_tracker.header) _tracker.header = previous_header tap.py-3.0/tap/tests/test_tracker.py0000644000076500000240000002576213606111604017663 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import inspect from io import StringIO import os import tempfile from unittest import mock from tap.i18n import _ from tap.tests import TestCase from tap.tracker import Tracker class TestTracker(TestCase): def _make_header(self, test_case): return _("# TAP results for {test_case}").format(test_case=test_case) def test_has_test_cases(self): tracker = Tracker() self.assertEqual(tracker._test_cases, {}) def test_tracks_class(self): tracker = Tracker() tracker._track("FakeTestClass") self.assertEqual(tracker._test_cases.get("FakeTestClass"), []) def test_adds_ok(self): tracker = Tracker() tracker.add_ok("FakeTestCase", "a description") line = tracker._test_cases["FakeTestCase"][0] self.assertTrue(line.ok) self.assertEqual(line.description, "a description") def test_adds_not_ok(self): tracker = Tracker() tracker.add_not_ok("FakeTestCase", "a description") line = tracker._test_cases["FakeTestCase"][0] self.assertFalse(line.ok) self.assertEqual(line.description, "a description") def test_adds_skip(self): tracker = Tracker() tracker.add_skip("FakeTestCase", "a description", "a reason") line = tracker._test_cases["FakeTestCase"][0] self.assertTrue(line.ok) self.assertEqual(line.description, "a description") self.assertEqual(line.directive.text, "SKIP a reason") def test_generates_tap_reports_in_new_outdir(self): tempdir = tempfile.mkdtemp() outdir = os.path.join(tempdir, "non", "existent", "path") tracker = Tracker(outdir=outdir) tracker.add_ok("FakeTestCase", "I should be in the specified dir.") tracker.generate_tap_reports() tap_file = os.path.join(outdir, "FakeTestCase.tap") self.assertTrue(os.path.exists(tap_file)) def test_generates_tap_reports_in_existing_outdir(self): outdir = tempfile.mkdtemp() tracker = Tracker(outdir=outdir) tracker.add_ok("FakeTestCase", "I should be in the specified dir.") tracker.generate_tap_reports() tap_file = os.path.join(outdir, "FakeTestCase.tap") self.assertTrue(os.path.exists(tap_file)) def test_results_not_combined_by_default(self): tracker = Tracker() self.assertFalse(tracker.combined) def test_individual_report_has_no_plan_when_combined(self): outdir = tempfile.mkdtemp() tracker = Tracker(outdir=outdir, combined=True) tracker.add_ok("FakeTestCase", "Look ma, no plan!") out_file = StringIO() tracker.generate_tap_report( "FakeTestCase", tracker._test_cases["FakeTestCase"], out_file ) report = out_file.getvalue() self.assertTrue("Look ma" in report) self.assertFalse("1.." in report) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_combined_results_in_one_file_tap_version_12(self): outdir = tempfile.mkdtemp() tracker = Tracker(outdir=outdir, combined=True) tracker.add_ok("FakeTestCase", "YESSS!") tracker.add_ok("DifferentFakeTestCase", "GOAAL!") tracker.generate_tap_reports() self.assertFalse(os.path.exists(os.path.join(outdir, "FakeTestCase.tap"))) self.assertFalse( os.path.exists(os.path.join(outdir, "DifferentFakeTestCase.tap")) ) with open(os.path.join(outdir, "testresults.tap"), "r") as f: report = f.read() expected = inspect.cleandoc( """{header_1} ok 1 YESSS! {header_2} ok 2 GOAAL! 1..2 """.format( header_1=self._make_header("FakeTestCase"), header_2=self._make_header("DifferentFakeTestCase"), ) ) self.assertEqual(report.strip(), expected) @mock.patch("tap.tracker.ENABLE_VERSION_13", True) def test_combined_results_in_one_file_tap_version_13(self): outdir = tempfile.mkdtemp() tracker = Tracker(outdir=outdir, combined=True) tracker.add_ok("FakeTestCase", "YESSS!") tracker.add_ok("DifferentFakeTestCase", "GOAAL!") tracker.generate_tap_reports() self.assertFalse(os.path.exists(os.path.join(outdir, "FakeTestCase.tap"))) self.assertFalse( os.path.exists(os.path.join(outdir, "DifferentFakeTestCase.tap")) ) with open(os.path.join(outdir, "testresults.tap"), "r") as f: report = f.read() expected = inspect.cleandoc( """ TAP version 13 {header_1} ok 1 YESSS! {header_2} ok 2 GOAAL! 1..2 """.format( header_1=self._make_header("FakeTestCase"), header_2=self._make_header("DifferentFakeTestCase"), ) ) self.assertEqual(report.strip(), expected) def test_tracker_does_not_stream_by_default(self): tracker = Tracker() self.assertFalse(tracker.streaming) def test_tracker_has_stream(self): tracker = Tracker() self.assertTrue(tracker.stream is None) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_add_ok_writes_to_stream_while_streaming(self): stream = StringIO() tracker = Tracker(streaming=True, stream=stream) tracker.add_ok("FakeTestCase", "YESSS!") tracker.add_ok("AnotherTestCase", "Sure.") expected = inspect.cleandoc( """{header_1} ok 1 YESSS! {header_2} ok 2 Sure. """.format( header_1=self._make_header("FakeTestCase"), header_2=self._make_header("AnotherTestCase"), ) ) self.assertEqual(stream.getvalue().strip(), expected) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_add_not_ok_writes_to_stream_while_streaming(self): stream = StringIO() tracker = Tracker(streaming=True, stream=stream) tracker.add_not_ok("FakeTestCase", "YESSS!") expected = inspect.cleandoc( """{header} not ok 1 YESSS! """.format( header=self._make_header("FakeTestCase") ) ) self.assertEqual(stream.getvalue().strip(), expected) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_add_skip_writes_to_stream_while_streaming(self): stream = StringIO() tracker = Tracker(streaming=True, stream=stream) tracker.add_skip("FakeTestCase", "YESSS!", "a reason") expected = inspect.cleandoc( """{header} ok 1 YESSS! # SKIP a reason """.format( header=self._make_header("FakeTestCase") ) ) self.assertEqual(stream.getvalue().strip(), expected) def test_streaming_does_not_write_files(self): outdir = tempfile.mkdtemp() stream = StringIO() tracker = Tracker(outdir=outdir, streaming=True, stream=stream) tracker.add_ok("FakeTestCase", "YESSS!") tracker.generate_tap_reports() self.assertFalse(os.path.exists(os.path.join(outdir, "FakeTestCase.tap"))) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_streaming_writes_plan(self): stream = StringIO() tracker = Tracker(streaming=True, stream=stream) tracker.combined_line_number = 42 tracker.generate_tap_reports() self.assertEqual(stream.getvalue(), "1..42\n") @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_write_plan_first_streaming(self): outdir = tempfile.mkdtemp() stream = StringIO() tracker = Tracker(outdir=outdir, streaming=True, stream=stream) tracker.set_plan(123) tracker.add_ok("FakeTestCase", "YESSS!") tracker.generate_tap_reports() self.assertEqual( stream.getvalue(), "1..123\n{header}\nok 1 YESSS!\n".format( header=self._make_header("FakeTestCase") ), ) self.assertFalse(os.path.exists(os.path.join(outdir, "FakeTestCase.tap"))) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_write_plan_immediate_streaming(self): stream = StringIO() Tracker(streaming=True, stream=stream, plan=123) self.assertEqual(stream.getvalue(), "1..123\n") @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_write_plan_first_combined(self): outdir = tempfile.mkdtemp() tracker = Tracker(streaming=False, outdir=outdir, combined=True) tracker.set_plan(123) tracker.generate_tap_reports() with open(os.path.join(outdir, "testresults.tap"), "r") as f: lines = f.readlines() self.assertEqual(lines[0], "1..123\n") @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_write_plan_first_not_combined(self): outdir = tempfile.mkdtemp() tracker = Tracker(streaming=False, outdir=outdir, combined=False) with self.assertRaises(ValueError): tracker.set_plan(123) @mock.patch("tap.tracker.ENABLE_VERSION_13", True) def test_streaming_writes_tap_version_13(self): stream = StringIO() tracker = Tracker(streaming=True, stream=stream) tracker.add_skip("FakeTestCase", "YESSS!", "a reason") expected = inspect.cleandoc( """ TAP version 13 {header} ok 1 YESSS! # SKIP a reason """.format( header=self._make_header("FakeTestCase") ) ) self.assertEqual(stream.getvalue().strip(), expected) def test_get_default_tap_file_path(self): tracker = Tracker() file_path = tracker._get_tap_file_path("foo") self.assertEqual("foo.tap", file_path) def test_sanitizes_tap_file_path(self): tracker = Tracker() file_path = tracker._get_tap_file_path("an awful \\ testcase / name\n") self.assertEqual("an-awful---testcase---name-.tap", file_path) def test_adds_not_ok_with_diagnostics(self): tracker = Tracker() tracker.add_not_ok("FakeTestCase", "a description", diagnostics="# more info\n") line = tracker._test_cases["FakeTestCase"][0] self.assertEqual("# more info\n", line.diagnostics) def test_header_displayed_by_default(self): tracker = Tracker() self.assertTrue(tracker.header) def test_header_set_by_init(self): tracker = Tracker(header=False) self.assertFalse(tracker.header) @mock.patch("tap.tracker.ENABLE_VERSION_13", False) def test_does_not_write_header(self): stream = StringIO() tracker = Tracker(streaming=True, stream=stream, header=False) tracker.add_skip("FakeTestCase", "YESSS!", "a reason") expected = inspect.cleandoc( """ ok 1 YESSS! # SKIP a reason """ ) self.assertEqual(stream.getvalue().strip(), expected) tap.py-3.0/tap/tests/testcase.py0000644000076500000240000000042213540027652016774 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import unittest from tap.tests.factory import Factory class TestCase(unittest.TestCase): def __init__(self, methodName="runTest"): super(TestCase, self).__init__(methodName) self.factory = Factory() tap.py-3.0/tap/tracker.py0000644000076500000240000001703613573576145015477 0ustar mattstaff00000000000000# Copyright (c) 2019, Matt Layman and contributors import os from tap.directive import Directive from tap.i18n import _ from tap.line import Result try: import more_itertools # noqa import yaml # noqa ENABLE_VERSION_13 = True except ImportError: # pragma: no cover ENABLE_VERSION_13 = False class Tracker(object): def __init__( self, outdir=None, combined=False, streaming=False, stream=None, header=True, plan=None, ): self.outdir = outdir # Combine all the test results into one file. self.combined = combined self.combined_line_number = 0 # Test case ordering is important for the combined results # because of how numbers are assigned. The test cases # must be tracked in order so that reporting can sequence # the line numbers properly. self.combined_test_cases_seen = [] # Stream output directly to a stream instead of file output. self.streaming = streaming self.stream = stream # The total number of tests we expect (or None if we don't know yet). self.plan = plan self._plan_written = False # Display the test case header unless told not to. self.header = header # Internal state for tracking each test case. self._test_cases = {} self._sanitized_table = str.maketrans(" \\/\n", "----") if self.streaming: self._write_tap_version(self.stream) if self.plan is not None: self._write_plan(self.stream) def _get_outdir(self): return self._outdir def _set_outdir(self, outdir): self._outdir = outdir if outdir and not os.path.exists(outdir): os.makedirs(outdir) outdir = property(_get_outdir, _set_outdir) def _track(self, class_name): """Keep track of which test cases have executed.""" if self._test_cases.get(class_name) is None: if self.streaming and self.header: self._write_test_case_header(class_name, self.stream) self._test_cases[class_name] = [] if self.combined: self.combined_test_cases_seen.append(class_name) def add_ok(self, class_name, description, directive=""): result = Result( ok=True, number=self._get_next_line_number(class_name), description=description, directive=Directive(directive), ) self._add_line(class_name, result) def add_not_ok(self, class_name, description, directive="", diagnostics=None): result = Result( ok=False, number=self._get_next_line_number(class_name), description=description, diagnostics=diagnostics, directive=Directive(directive), ) self._add_line(class_name, result) def add_skip(self, class_name, description, reason): directive = "SKIP {0}".format(reason) result = Result( ok=True, number=self._get_next_line_number(class_name), description=description, directive=Directive(directive), ) self._add_line(class_name, result) def _add_line(self, class_name, result): self._track(class_name) if self.streaming: print(result, file=self.stream) self._test_cases[class_name].append(result) def _get_next_line_number(self, class_name): if self.combined or self.streaming: # This has an obvious side effect. Oh well. self.combined_line_number += 1 return self.combined_line_number else: try: return len(self._test_cases[class_name]) + 1 except KeyError: # A result is created before the call to _track so the test # case may not be tracked yet. In that case, the line is 1. return 1 def set_plan(self, total): """Notify the tracker how many total tests there will be.""" self.plan = total if self.streaming: # This will only write the plan if we haven't written it # already but we want to check if we already wrote a # test out (in which case we can't just write the plan out # right here). if not self.combined_test_cases_seen: self._write_plan(self.stream) elif not self.combined: raise ValueError( "set_plan can only be used with combined or streaming output" ) def generate_tap_reports(self): """Generate TAP reports. The results are either combined into a single output file or the output file name is generated from the test case. """ if self.streaming: # We're streaming but set_plan wasn't called, so we can only # know the plan now (at the end). if not self._plan_written: print("1..{0}".format(self.combined_line_number), file=self.stream) self._plan_written = True return if self.combined: combined_file = "testresults.tap" if self.outdir: combined_file = os.path.join(self.outdir, combined_file) with open(combined_file, "w") as out_file: self._write_tap_version(out_file) if self.plan is not None: print("1..{0}".format(self.plan), file=out_file) for test_case in self.combined_test_cases_seen: self.generate_tap_report( test_case, self._test_cases[test_case], out_file ) if self.plan is None: print("1..{0}".format(self.combined_line_number), file=out_file) else: for test_case, tap_lines in self._test_cases.items(): with open(self._get_tap_file_path(test_case), "w") as out_file: self._write_tap_version(out_file) self.generate_tap_report(test_case, tap_lines, out_file) def generate_tap_report(self, test_case, tap_lines, out_file): self._write_test_case_header(test_case, out_file) for tap_line in tap_lines: print(tap_line, file=out_file) # For combined results, the plan is only output once after # all the test cases complete. if not self.combined: print("1..{0}".format(len(tap_lines)), file=out_file) def _write_tap_version(self, filename): """Write a Version 13 TAP row. ``filename`` can be a filename or a stream. """ if ENABLE_VERSION_13: print("TAP version 13", file=filename) def _write_plan(self, stream): """Write the plan line to the stream. If we have a plan and have not yet written it out, write it to the given stream. """ if self.plan is not None: if not self._plan_written: print("1..{0}".format(self.plan), file=stream) self._plan_written = True def _write_test_case_header(self, test_case, stream): print( _("# TAP results for {test_case}").format(test_case=test_case), file=stream ) def _get_tap_file_path(self, test_case): """Get the TAP output file path for the test case.""" sanitized_test_case = test_case.translate(self._sanitized_table) tap_file = sanitized_test_case + ".tap" if self.outdir: return os.path.join(self.outdir, tap_file) return tap_file tap.py-3.0/tap.py.egg-info/0000755000076500000240000000000013606112130015555 5ustar mattstaff00000000000000tap.py-3.0/tap.py.egg-info/PKG-INFO0000644000076500000240000002035013606112130016652 0ustar mattstaff00000000000000Metadata-Version: 2.1 Name: tap.py Version: 3.0 Summary: Test Anything Protocol (TAP) tools Home-page: https://github.com/python-tap/tappy Author: Matt Layman Author-email: matthewlayman@gmail.com License: BSD Description: tappy is a set of tools for working with the `Test Anything Protocol (TAP) `_, a line based test protocol for recording test data in a standard way. Follow tappy development on `GitHub `_. Developer documentation is on `Read the Docs `_. Releases ======== Version 3.0, Released January 10, 2020 -------------------------------------- * Drop support for Python 2 (it is end-of-life). * Add support for subtests. * Run a test suite with ``python -m tap``. * Discontinue use of Pipenv for managing development. Version 2.6.2, Released October 20, 2019 ---------------------------------------- * Fix bug in streaming mode that would generate tap files when the plan was already set (affected pytest). Version 2.6.1, Released September 17, 2019 ------------------------------------------ * Fix TAP version 13 support from more-itertools behavior change. Version 2.6, Released September 16, 2019 ---------------------------------------- * Add support for Python 3.7. * Drop support for Python 3.4 (it is end-of-life). Version 2.5, Released September 15, 2018 ---------------------------------------- * Add ``set_plan`` to ``Tracker`` which allows producing the ``1..N`` plan line before any tests. * Switch code style to use Black formatting. Version 2.4, Released May 29, 2018 ---------------------------------- * Add support for producing TAP version 13 output to streaming and file reports by including the ``TAP version 13`` line. Version 2.3, Released May 15, 2018 ---------------------------------- * Add optional method to install tappy for YAML support with ``pip install tap.py[yaml]``. * Make tappy version 13 compliant by adding support for parsing YAML blocks. * ``unittest.expectedFailure`` now uses a TODO directive to better align with the specification. Version 2.2, Released January 7, 2018 ------------------------------------- * Add support for Python 3.6. * Drop support for Python 3.3 (it is end-of-life). * Use Pipenv for managing development. * Switch to pytest as the development test runner. Version 2.1, Released September 23, 2016 ---------------------------------------- * Add ``Parser.parse_text`` to parse TAP provided as a string. Version 2.0, Released July 31, 2016 ----------------------------------- * Remove nose plugin. The plugin moved to the ``nose-tap`` distribution. * Remove pytest plugin. The plugin moved to the ``pytest-tap`` distribution. * Remove Pygments syntax highlighting plugin. The plugin was merged upstream directly into the Pygments project and is available without tappy. * Drop support for Python 2.6. Version 1.9, Released March 28, 2016 ------------------------------------ * ``TAPTestRunner`` has a ``set_header`` method to enable or disable test case header ouput in the TAP stream. * Add support for Python 3.5. * Perform continuous integration testing on OS X. * Drop support for Python 3.2. Version 1.8, Released November 30, 2015 --------------------------------------- * The ``tappy`` TAP consumer can read a TAP stream directly from STDIN. * Tracebacks are included as diagnostic output for failures and errors. * The ``tappy`` TAP consumer has an alternative, shorter name of ``tap``. * The pytest plugin now defaults to no output unless provided a flag. Users dependent on the old default behavior can use ``--tap-files`` to achieve the same results. * Translated into Arabic. * Translated into Chinese. * Translated into Japanese. * Translated into Russian. * Perform continuous integration testing on Windows with AppVeyor. * Improve unit test coverage to 100%. Version 1.7, Released August 19, 2015 ------------------------------------- * Provide a plugin to integrate with pytest. * Document some viable alternatives to tappy. * Translated into German. * Translated into Portuguese. Version 1.6, Released June 18, 2015 ----------------------------------- * ``TAPTestRunner`` has a ``set_stream`` method to stream all TAP output directly to an output stream instead of a file. results in a single output file. * The ``nosetests`` plugin has an optional ``--tap-stream`` flag to stream all TAP output directly to an output stream instead of a file. * tappy is now internationalized. It is translated into Dutch, French, Italian, and Spanish. * tappy is available as a Python wheel package, the new Python packaging standard. Version 1.5, Released May 18, 2015 ---------------------------------- * ``TAPTestRunner`` has a ``set_combined`` method to collect all results in a single output file. * The ``nosetests`` plugin has an optional ``--tap-combined`` flag to collect all results in a single output file. * ``TAPTestRunner`` has a ``set_format`` method to specify line format. * The ``nosetests`` plugin has an optional ``--tap-format`` flag to specify line format. Version 1.4, Released April 4, 2015 ----------------------------------- * Update ``setup.py`` to support Debian packaging. Include man page. Version 1.3, Released January 9, 2015 ------------------------------------- * The ``tappy`` command line tool is available as a TAP consumer. * The ``Parser`` and ``Loader`` are available as APIs for programmatic handling of TAP files and data. Version 1.2, Released December 21, 2014 --------------------------------------- * Provide a syntax highlighter for Pygments so any project using Pygments (e.g., Sphinx) can highlight TAP output. Version 1.1, Released October 23, 2014 -------------------------------------- * ``TAPTestRunner`` has a ``set_outdir`` method to specify where to store ``.tap`` files. * The ``nosetests`` plugin has an optional ``--tap-outdir`` flag to specify where to store ``.tap`` files. * tappy has backported support for Python 2.6. * tappy has support for Python 3.2, 3.3, and 3.4. * tappy has support for PyPy. Version 1.0, Released March 16, 2014 ------------------------------------ * Initial release of tappy * ``TAPTestRunner`` - A test runner for ``unittest`` modules that generates TAP files. * Provides a plugin for integrating with **nose**. Keywords: TAP,unittest Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Testing Provides-Extra: yaml tap.py-3.0/tap.py.egg-info/SOURCES.txt0000644000076500000240000000345613606112130017451 0ustar mattstaff00000000000000AUTHORS LICENSE MANIFEST.in README.md setup.cfg setup.py docs/Makefile docs/alternatives.rst docs/conf.py docs/consumers.rst docs/contributing.rst docs/highlighter.rst docs/index.rst docs/make.bat docs/producers.rst docs/releases.rst docs/sample_tap.txt docs/tappy.1.rst docs/_static/.keep docs/images/python-tap.png docs/images/stream.gif docs/images/tap.png tap/__init__.py tap/__main__.py tap/adapter.py tap/directive.py tap/formatter.py tap/i18n.py tap/line.py tap/loader.py tap/main.py tap/parser.py tap/rules.py tap/runner.py tap/tracker.py tap.py.egg-info/PKG-INFO tap.py.egg-info/SOURCES.txt tap.py.egg-info/dependency_links.txt tap.py.egg-info/entry_points.txt tap.py.egg-info/not-zip-safe tap.py.egg-info/requires.txt tap.py.egg-info/top_level.txt tap/locale/tappy.pot tap/locale/ar/LC_MESSAGES/tappy.mo tap/locale/ar/LC_MESSAGES/tappy.po tap/locale/de/LC_MESSAGES/tappy.mo tap/locale/de/LC_MESSAGES/tappy.po tap/locale/es/LC_MESSAGES/tappy.mo tap/locale/es/LC_MESSAGES/tappy.po tap/locale/fr/LC_MESSAGES/tappy.mo tap/locale/fr/LC_MESSAGES/tappy.po tap/locale/it/LC_MESSAGES/tappy.mo tap/locale/it/LC_MESSAGES/tappy.po tap/locale/ja/LC_MESSAGES/tappy.mo tap/locale/ja/LC_MESSAGES/tappy.po tap/locale/nl/LC_MESSAGES/tappy.mo tap/locale/nl/LC_MESSAGES/tappy.po tap/locale/pt/LC_MESSAGES/tappy.mo tap/locale/pt/LC_MESSAGES/tappy.po tap/locale/ru/LC_MESSAGES/tappy.mo tap/locale/ru/LC_MESSAGES/tappy.po tap/locale/zh/LC_MESSAGES/tappy.mo tap/locale/zh/LC_MESSAGES/tappy.po tap/tests/__init__.py tap/tests/factory.py tap/tests/run.py tap/tests/test_adapter.py tap/tests/test_directive.py tap/tests/test_formatter.py tap/tests/test_line.py tap/tests/test_loader.py tap/tests/test_main.py tap/tests/test_parser.py tap/tests/test_result.py tap/tests/test_rules.py tap/tests/test_runner.py tap/tests/test_tracker.py tap/tests/testcase.pytap.py-3.0/tap.py.egg-info/dependency_links.txt0000644000076500000240000000000113606112130021623 0ustar mattstaff00000000000000 tap.py-3.0/tap.py.egg-info/entry_points.txt0000644000076500000240000000007513606112130021055 0ustar mattstaff00000000000000[console_scripts] tap = tap.main:main tappy = tap.main:main tap.py-3.0/tap.py.egg-info/not-zip-safe0000644000076500000240000000000113224310277020013 0ustar mattstaff00000000000000 tap.py-3.0/tap.py.egg-info/requires.txt0000644000076500000240000000004313606112130020152 0ustar mattstaff00000000000000 [yaml] more-itertools PyYAML>=5.1 tap.py-3.0/tap.py.egg-info/top_level.txt0000644000076500000240000000000413606112130020301 0ustar mattstaff00000000000000tap