././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1747294461.876767 zope_testrunner-7.3/0000755000076600000240000000000015011314376014335 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/.pre-commit-config.yaml0000644000076600000240000000141715011314374020617 0ustar00m.howitzstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python minimum_pre_commit_version: '3.6' repos: - repo: https://github.com/pycqa/isort rev: "6.0.0" hooks: - id: isort - repo: https://github.com/hhatto/autopep8 rev: "v2.3.2" hooks: - id: autopep8 args: [--in-place, --aggressive, --aggressive] - repo: https://github.com/asottile/pyupgrade rev: v3.19.1 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/isidentical/teyit rev: 0.4.3 hooks: - id: teyit exclude: tests/testrunner-ex/sample2/badsyntax\.py - repo: https://github.com/PyCQA/flake8 rev: "7.1.1" hooks: - id: flake8 additional_dependencies: - flake8-debugger == 4.1.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/.readthedocs.yaml0000644000076600000240000000123515011314374017563 0ustar00m.howitzstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt - method: pip path: . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/CHANGES.rst0000644000076600000240000004255015011314374016143 0ustar00m.howitzstaff=========================== zope.testrunner Changelog =========================== 7.3 (2025-05-15) ================ - Improve ``@unittest.expectedFailure`` support, especially regarding post-mortem debugger. (`#196 `_) 7.2 (2025-03-06) ================ - Re-add a single import of ``pkg_resources`` to avoid other import issues in mixed pip/buildout environments. (`#194 `_) 7.1 (2025-03-05) ================ - Replace ``pkg_resources`` with ``importlib.metadata``. 7.0 (2025-02-12) ================ Backwards-incompatible changes ------------------------------ - Remove ``setup.py ftest`` command. (`#178 `_) - Remove ``zope.testrunner.eggsupport``. It is no longer usable as of ``setuptools`` 72.0.0. (`#185 `_) 6.7 (2025-02-07) ================ - Drop support for Python 3.8. - Add option ``--only-level=level`` to run tests only at the specified level. (`#188 `_) 6.6.1 (2024-12-13) ================== - Make signatures in ``tb_format`` Python 3.12+ compatible (`#186 `_) 6.6 (2024-10-16) ================ - Make tests compatible with Python 3.13 + add support for that version. (`#181 `_) - Drop support for Python 3.7. 6.5 (2024-08-06) ================ - Remove setuptools fossils. - ``unittest.TestCase.subTest`` support (`#91 `_). - remove support for ``setup.py``'s ``test`` command. Support for this command has been dropped by modern ``setuptools`` versions and correspondingly has been removed from most ``zopefoundation`` packages; ``zope.testrunner`` now follows. - ``setup.py``'s ``ftest`` command is now only supported when the used ``setuptools`` version still supports ``test``. 6.4 (2024-02-27) ================ - Add PEP 420 support (implicit namespaces). (`#160 `_) 6.3.1 (2024-02-12) ================== - Fix XML tests when running in distribution resp. separately. (`#163 `_) 6.3 (2024-02-07) ================ - Exit cleanly when using the test runner ``--version`` argument. (`#102 `_) - Add new ``--xml `` option to write JUnit-like XML reports. Code comes from ``collective.xmltestreport``, but be aware that here ``--xml`` is not a boolean, but expects a path! (`#148 `_). - Add support for Python 3.13a3. 6.2.1 (2023-12-22) ================== - Work around Python 3.12.1+ no longer calling ``startTest`` for skipped tests (`#157 `_). 6.2 (2023-11-08) ================ - Add support for Python 3.12. - Update code and tests to ``python-subunit >= 1.4.3`` thus requiring at least this version. 6.1 (2023-08-26) ================ - Add preliminary support for Python 3.12b4. (`#149 `_) 6.0 (2023-03-28) ================ - Drop support for Python 2.7, 3.5, 3.6. 5.6 (2022-12-09) ================ - Add support for Python 3.11. - Inline a small part of ``random.Random.shuffle`` which was deprecated in Python 3.9 and removed in 3.11 (`#119 `_). - Don't trigger post mortem debugger for skipped tests. ( `#141 `_). 5.5.1 (2022-09-07) ================== - Fix: let ``--at-level=level`` with ``level <= 0`` run the tests at all levels (rather than at no level) `#138 `_. 5.5 (2022-06-24) ================ - Use ``sys._current_frames`` (rather than ``threading.enumerate``) as base for new thread detection, fixes `#130 `_. - New option ``--gc-after-test``. It calls for a garbage collection after each test and can be used to track down ``ResourceWarning``s and cyclic garbage. With ``rv = gc.collect()``, ``!`` is output on verbosity level 1 when ``rv`` is non zero (i.e. when cyclic structures have been released), ``[``*rv*``]`` on higher verbosity levels and a detailed cyclic garbage analysis on verbosity level 4+. For details, see `#133 `_. - Allow the filename for the logging configuration to be specified via the envvar ``ZOPE_TESTRUNNER_LOG_INI``. If not defined, the configuration continues to be locked for in file ``log.ini`` of the current working directory. Remember the logging configuration file in envvar ``ZOPE_TESTRUNNER_LOG_INI`` to allow spawned child processes to recreate the logging configuration. For details, see `#134 `_. 5.4.0 (2021-11-19) ================== - Improve ``--help`` documentation for ``--package-path`` option (`#121 `_). - Do not disable existing loggers during logsupport initialization (`#120 `_). - Fix tests with testtools >= 2.5.0 (`#125 `_). - Add support for Python 3.10. 5.3.0 (2021-03-17) ================== - Add support for Python 3.9. - Fix `package init file missing` warning (`#112 `_). - Make standard streams provide a `buffer` attribute on Python 3 when using `--buffer` or testing under subunit. 5.2 (2020-06-29) ================ - Add support for Python 3.8. - When a layer is run in a subprocess, read its stderr in a thread to avoid a deadlock if its stderr output (containing failing and erroring test IDs) overflows the capacity of a pipe (`#105 `_). 5.1 (2019-10-19) ================ - Recover more gracefully when layer setUp or tearDown fails, producing useful subunit output. - Prevent a spurious warning from the ``--require-unique`` option if the ``--module`` option was not used. - Add optional buffering of standard output and standard error during tests, requested via the ``--buffer`` option or enabled by default for subunit. - Fix incorrect failure counts in per-layer summary output, broken in 4.0.1. 5.0 (2019-03-19) ================ - Fix test failures and deprecation warnings occurring when using Python 3.8a1. (`#89 `_) - Drop support for Python 3.4. 4.9.2 (2018-11-24) ================== - Fix ``TypeError: a bytes-like object is required, not 'str'`` running tests in parallel on Python 3. See `issue 80 `_. 4.9.1 (2018-11-21) ================== - Fix AssertionError in _DummyThread.isAlive on Python 3 (`#81 `_). 4.9 (2018-10-05) ================ - Drop support for Python 3.3. - Add support for Python 3.7. - Enable test coverage reporting on coveralls.io and in tox.ini. - Host documentation at https://zopetestrunner.readthedocs.io - Remove untested support for the ``--pychecker`` option. See `issue 63 `_. - Update the command line interface to use ``argparse`` instead of ``optparse``. See `issue 61 `_. - Use ipdb instead of pdb for post-mortem debugging if available (`#10 `_). - Add a --require-unique option to check for duplicate test IDs. See `LP #682771 `_. - Reintroduce optional support for ``subunit``, now with support for both version 1 and version 2 of its protocol. - Handle string in exception values when formatting chained exceptions. (`#74 `_) 4.8.1 (2017-11-12) ================== - Enable ``DeprecationWarning`` earlier, when discovering test modules. This lets warnings that are raised on import (such as those produced by ``zope.deprecation.moved``) be reported. See `issue 57 `_. 4.8.0 (2017-11-10) ================== - Automatically enable ``DeprecationWarning`` when running tests. This is recommended by the Python core developers and matches the behaviour of the ``unittest`` module. This can be overridden with Python command-line options (``-W``) or environment variables (``PYTHONWARNINGS``). See `issue 54 `_. 4.7.0 (2017-05-30) ================== - Drop all support for ``subunit``. 4.6.0 (2016-12-28) ================== - Make the ``subunit`` support purely optional: applications which have been getting the dependencies via ``zope.testrunner`` should either add ``zope.testrunner[subunit]`` to their ``install_requires`` or else depend directly on ``python-subunit``. - New option ``--ignore-new-thread=`` to suppress "New thread(s)" warnings. - Support Python 3.6. 4.5.1 (2016-06-20) ================== - Fixed: Using the ``-j`` option to run tests in multiple processes caused tests that used the ``multiprocessing`` package to hang (because the testrunner replaced ``sys.stdin`` with an unclosable object). - Drop conditional dependency on ``unittest2`` (redundant after dropping support for Python 2.6). 4.5.0 (2016-05-02) ================== - Stop tests for all layers when test fails/errors when started with -x/--stop-on-error (`#37 `_). - Drop support for Python 2.6 and 3.2. 4.4.10 (2015-11-10) =================== - Add support for Python 3.5 (`#31 `_). - Insert extra paths (from ``--path``) to the front of sys.argv (`#32 `_). 4.4.9 (2015-05-21) ================== - When using ``-j``, parallelize all the tests, including the first test layer (`#28 `_). 4.4.8 (2015-05-01) ================== - Support skipped tests in subunit output (`#25 `_). - More efficient test filtering (`#26 `_). 4.4.7 (2015-04-02) ================== - Work around a bug in PyPy3's curses module (`#24 `_). 4.4.6 (2015-01-21) ================== - Restore support for instance-based test layers that regressed in 4.4.5 (`#20 `_). 4.4.5 (2015-01-06) ================== - Sort related layers close to each other to reduce the number of unnecessary teardowns (fixes `#14 `_). - Run the unit test layer first (fixes `LP #497871 `__). 4.4.4 (2014-12-27) ================== - When looking for the right location of test code, start with longest location paths first. This fixes problems with nested code locations. 4.4.3 (2014-03-19) ================== - Added support for Python 3.4. 4.4.2 (2014-02-22) ================== - Drop support for Python 3.1. - Fix post-mortem debugging when a non-printable exception happens (https://github.com/zopefoundation/zope.testrunner/issues/8). 4.4.1 (2013-07-10) ================== - Updated ``boostrap.py`` to version 2.2. - Fix nondeterministic test failures on Python 3.3 - Tear down layers after ``post_mortem`` debugging is finished. - Fix tests that write to source directory, it might be read-only. 4.4.0 (2013-06-06) ================== - Fix tests selection when the negative "!" pattern is used several times (LP #1160965) - Moved tests into a 'tests' subpackage. - Made ``python -m zope.testrunner`` work again. - Support 'skip' feature of unittest2 (which became the new unittest in Python 2.7). - Better diagnostics when communication with subprocess fails (https://github.com/zopefoundation/zope.testrunner/issues/5). - Do not break subprocess execution when the test suite changes the working directory (https://github.com/zopefoundation/zope.testrunner/issues/6). - Count test module import errors as errors (LP #1026576). 4.3.3 (2013-03-03) ================== - Running layers in sub-processes did not use to work when run via ``python setup.py ftest`` since it tried to run setup.py with all the command line options. It now detects ``setup.py`` runs and we run the test runner directly. 4.3.2 (2013-03-03) ================== - Fix ``SkipLayers`` class in cases where the distribution specifies a ``test_suite`` value. 4.3.1 (2013-03-02) ================== - Fixed a bug in the `ftest` command and added a test. - Fixed a trivial test failure with Python 3 of the previous release. 4.3.0 (2013-03-02) ================== - Expose `ftest` distutils command via an entry point. - Added tests for ``zope.testrunner.eggsupport``. 4.2.0 (2013-02-12) ================== - Dropped use of 2to3, rewrote source code to be compatible with all Python versions. Introduced a dependency on `six`_. 4.1.1 (2013-02-08) ================== - Dropped use of zope.fixers (LP: #1118877). - Fixed tox test error reporting; fixed tests on Pythons 2.6, 3.1, 3.2, 3.3 and PyPy 1.9. - Fix --shuffle ordering on Python 3.2 to be the same as it was on older Python versions. - Fix --shuffle nondeterminism when multiple test layers are present. Note: this will likely change the order of tests for the same --shuffle-seed. - New option: --profile-directory. Use it in the test suite so that tests executed by detox in parallel don't conflict. - Use a temporary coverage directory in the test suite so that tests executed by detox in parallel don't conflict. - Fix --post-mortem (aka -D, --pdb) when a test module cannot be imported or is invalid (LP #1119363). 4.1.0 (2013-02-07) ================== - Replaced deprecated ``zope.interface.implements`` usage with equivalent ``zope.interface.implementer`` decorator. - Dropped support for Python 2.4 and 2.5. - Made StartUpFailure compatible with unittest.TextTestRunner() (LP #1118344). 4.0.4 (2011-10-25) ================== - Work around sporadic timing-related issues in the subprocess buffering tests. Thanks to Jonathan Ballet for the patch! 4.0.3 (2011-03-17) ================== - Added back support for Python <= 2.6 which was broken in 4.0.2. 4.0.2 (2011-03-16) ================== - Added back Python 3 support which was broken in 4.0.1. - Fixed `Unexpected success`_ support by implementing the whole concept. - Added support for the new __pycache__ directories in Python 3.2. 4.0.1 (2011-02-21) ================== - LP #719369: An `Unexpected success`_ (concept introduced in Python 2.7) is no longer handled as success but as failure. This is a workaround. The whole unexpected success concept might be implemented later. .. _`Unexpected success`: http://www.voidspace.org.uk/python/articles/unittest2.shtml#more-skipping 4.0.0 (2010-10-19) ================== - Show more information about layers whose setup fails (LP #638153). 4.0.0b5 (2010-07-20) ==================== - Update fix for LP #221151 to a spelling compatible with Python 2.4. - Timestamps are now always included in subunit output (r114849). - LP #591309: fix a crash when subunit reports test failures containing UTF8-encoded data. 4.0.0b4 (2010-06-23) ==================== - Package as a zipfile to work around Python 2.4 distutils bug (no feature changes or bugfixes in ``zope.testrunner`` itself). 4.0.0b3 (2010-06-16) ==================== - LP #221151: keep ``unittest.TestCase.shortDescription`` happy by supplying a ``_testMethodDoc`` attribute. - LP #595052: keep the distribution installable under Python 2.4: its distutils appears to munge the empty ``__init__.py`` file in the ``foo.bar`` egg used for testing into a directory. - LP #580083: fix the ``bin/test`` script to run only tests from ``zope.testrunner``. - LP #579019: When layers were run in parallel, their tearDown was not called. Additionally, the first layer which was run in the main thread did not have its tearDown called either. 4.0.0b2 (2010-05-03) ==================== - Having 'sampletests' in the MANIFEST.in gave warnings, but doesn't actually seem to include any more files, so I removed it. - Moved zope.testing.exceptions to zope.testrunner.exceptions. Now zope.testrunner no longer requires zope.testing except for when running its own tests. 4.0.0b1 (2010-04-29) ==================== - Initial release of the testrunner from zope.testrunner as its own module. (Previously it was part of zope.testing.) .. _six: http://pypi.python.org/pypi/six ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/CONTRIBUTING.md0000644000076600000240000000144315011314374016566 0ustar00m.howitzstaff # Contributing to zopefoundation projects The projects under the zopefoundation GitHub organization are open source and welcome contributions in different forms: * bug reports * code improvements and bug fixes * documentation improvements * pull request reviews For any changes in the repository besides trivial typo fixes you are required to sign the contributor agreement. See https://www.zope.dev/developer/becoming-a-committer.html for details. Please visit our [Developer Guidelines](https://www.zope.dev/developer/guidelines.html) if you'd like to contribute code changes and our [guidelines for reporting bugs](https://www.zope.dev/developer/reporting-bugs.html) if you want to file a bug report. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/COPYRIGHT.rst0000644000076600000240000000004015011314374016427 0ustar00m.howitzstaffZope Foundation and Contributors././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/DEVELOPING.rst0000644000076600000240000000572415011314374016531 0ustar00m.howitzstaff************************** developing zope.testrunner ************************** Zope testrunner needs itself to run its own tests. There are two ways to do that. Using zc.buildout ----------------- The standard way to set up a testrunner to test zope.testrunner with, is to use buildout:: $ python bootstrap.py $ bin/buildout You can now run the tests:: $ bin/test -pvc Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in 0.000 seconds. Ran 28 tests with 0 failures and 0 errors in 17.384 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in 0.000 seconds. Using setup.py -------------- You may run the tests without buildout as well, as the setup.py has a custom test command that will run the tests:: $ python setup.py test running test running egg_info writing requirements to src/zope.testrunner.egg-info/requires.txt writing src/zope.testrunner.egg-info/PKG-INFO writing namespace_packages to src/zope.testrunner.egg-info/namespace_packages.txt writing top-level names to src/zope.testrunner.egg-info/top_level.txt writing dependency_links to src/zope.testrunner.egg-info/dependency_links.txt writing entry points to src/zope.testrunner.egg-info/entry_points.txt reading manifest template 'MANIFEST.in' warning: no files found matching 'sampletests' under directory 'src' writing manifest file 'src/zope.testrunner.egg-info/SOURCES.txt' running build_ext Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in 0.000 seconds. Ran 27 tests with 0 failures and 0 errors in 17.600 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in 0.000 seconds. Using tox/detox --------------- This is a convenient way to run the test suite for all supported Python versions, either sequentially (`tox`_) or in parallel (`detox`_):: $ detox GLOB sdist-make: /home/mg/src/zope.testrunner/setup.py py33 sdist-reinst: .../.tox/dist/zope.testrunner-4.1.2.dev0.zip py27 sdist-reinst: .../.tox/dist/zope.testrunner-4.1.2.dev0.zip pypy sdist-reinst: .../.tox/dist/zope.testrunner-4.1.2.dev0.zip py31 sdist-reinst: .../.tox/dist/zope.testrunner-4.1.2.dev0.zip py32 sdist-reinst: .../.tox/dist/zope.testrunner-4.1.2.dev0.zip py26 sdist-reinst: .../.tox/dist/zope.testrunner-4.1.2.dev0.zip py27 runtests: commands[0] py26 runtests: commands[0] pypy runtests: commands[0] py32 runtests: commands[0] py33 runtests: commands[0] py31 runtests: commands[0] ______________________________ summary _______________________________ py26: commands succeeded py27: commands succeeded py31: commands succeeded py32: commands succeeded py33: commands succeeded pypy: commands succeeded congratulations :) .. _tox: http://pypi.python.org/pypi/tox .. _detox: http://pypi.python.org/pypi/detox ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/LICENSE.md0000644000076600000240000000402615011314374015741 0ustar00m.howitzstaffZope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED 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 HOLDERS 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. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/MANIFEST.in0000644000076600000240000000065715011314374016101 0ustar00m.howitzstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python include *.md include *.rst include *.txt include buildout.cfg include tox.ini include .pre-commit-config.yaml recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.txt recursive-include docs Makefile recursive-include src *.py include *.md include *.yaml recursive-include src *.rst recursive-include src *.ini ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8766644 zope_testrunner-7.3/PKG-INFO0000644000076600000240000006026315011314376015441 0ustar00m.howitzstaffMetadata-Version: 2.1 Name: zope.testrunner Version: 7.3 Summary: Zope testrunner script. Home-page: https://github.com/zopefoundation/zope.testrunner Author: Zope Foundation and Contributors Author-email: zope-dev@zope.dev License: ZPL-2.1 Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Framework :: Zope :: 3 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Testing Requires-Python: >=3.9 License-File: LICENSE.md Requires-Dist: setuptools Requires-Dist: zope.exceptions Requires-Dist: zope.interface Provides-Extra: test Requires-Dist: zope.testing; extra == "test" Provides-Extra: subunit Requires-Dist: python-subunit>=1.4.3; extra == "subunit" Requires-Dist: testtools>=0.9.30; extra == "subunit" Provides-Extra: docs Requires-Dist: Sphinx; extra == "docs" Requires-Dist: sphinxcontrib-programoutput; extra == "docs" ================= zope.testrunner ================= .. image:: https://img.shields.io/pypi/v/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Supported Python versions .. image:: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.testrunner/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.testrunner?branch=master .. image:: https://readthedocs.org/projects/zopetestrunner/badge/?version=latest :target: https://zopetestrunner.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. contents:: This package provides a flexible test runner with layer support. Detailed documentation is hosted at https://zopetestrunner.readthedocs.io ======================= Using zope.testrunner ======================= .. IMPORTANT: This document is included in the long_description for rendering on PyPI, so it cannot use Sphinx-only features. Installation ============ Buildout-based projects ----------------------- zope.testrunner is often used for projects that use buildout_:: [buildout] develop = . parts = ... test ... [test] recipe = zc.recipe.testrunner eggs = mypackage The usual buildout process :: python bootstrap.py bin/buildout creates a ``bin/test`` script that will run the tests for *mypackage*. .. tip:: zc.recipe.testrunner_ takes care to specify the right ``--test-path`` option in the generated script. You can add other options (such as ``--tests-pattern``) too; check zc.recipe.testrunner_'s documentation for details. Virtualenv-based projects ------------------------- ``pip install zope.testrunner`` and you'll get a ``zope-testrunner`` script. Run your tests with :: zope-testrunner --test-path=path/to/your/source/tree Your source code needs to be available for the testrunner to import, so you need to run ``python setup.py install`` or ``pip install -e .`` into the same virtualenv_. Some useful command-line options to get you started =================================================== -p show a progress indicator -v increase verbosity -c colorize the output -t test specify test names (one or more regexes) -m module specify test modules (one or more regexes) -s package specify test packages (one or more regexes) --list-tests show names of tests instead of running them -x stop on first error or failure -D, --pdb enable post-mortem debugging of test failures --xml path generate XML reports to be written at the given path --help show *all* command-line options (there are many more!) For example :: bin/test -pvc -m test_foo -t TestBar runs all TestBar tests from a module called test_foo.py. Writing tests ============= ``zope.testrunner`` expects to find your tests inside your package directory, in a subpackage or module named ``tests``. Test modules in a test subpackage should be named ``test*.py``. .. tip:: You can change these assumptions with ``--tests-pattern`` and ``--test-file-pattern`` test runner options. Tests themselves should be classes inheriting from ``unittest.TestCase``, and if you wish to use doctests, please tell the test runner where to find them and what options to use for them in by supplying a function named ``test_suite``. Example:: import unittest import doctest class TestArithmetic(unittest.TestCase): def test_two_plus_two(self): self.assertEqual(2 + 2, 4) def doctest_string_formatting(): """Test Python string formatting >>> print('{} + {}'.format(2, 2)) 2 + 2 """ def test_suite(): return unittest.TestSuite([ unittest.defaultTestLoader.loadTestsFromName(__name__), doctest.DocTestSuite(), doctest.DocFileSuite('../README.txt', optionflags=doctest.ELLIPSIS), ]) Test grouping ============= In addition to per-package and per-module filtering, zope.testrunner has other mechanisms for grouping tests: * **layers** allow you to have shared setup/teardown code to be used by a group of tests, that is executed only once, and not for each test. Layers are orthogonal to the usual package/module structure and are specified by setting the ``layer`` attribute on test suites. * **levels** allow you to group slow-running tests and not run them by default. They're specified by setting the ``level`` attribute on test suites to an int. Other features ============== zope.testrunner can profile your tests, measure test coverage, check for memory leaks, integrate with subunit_, shuffle the test execution order, and run multiple tests in parallel. .. _buildout: https://buildout.readthedocs.io .. _virtualenv: https://virtualenv.pypa.io/ .. _zc.recipe.testrunner: https://pypi.python.org/pypi/zc.recipe.testrunner .. _subunit: https://pypi.python.org/pypi/python-subunit =========================== zope.testrunner Changelog =========================== 7.3 (2025-05-15) ================ - Improve ``@unittest.expectedFailure`` support, especially regarding post-mortem debugger. (`#196 `_) 7.2 (2025-03-06) ================ - Re-add a single import of ``pkg_resources`` to avoid other import issues in mixed pip/buildout environments. (`#194 `_) 7.1 (2025-03-05) ================ - Replace ``pkg_resources`` with ``importlib.metadata``. 7.0 (2025-02-12) ================ Backwards-incompatible changes ------------------------------ - Remove ``setup.py ftest`` command. (`#178 `_) - Remove ``zope.testrunner.eggsupport``. It is no longer usable as of ``setuptools`` 72.0.0. (`#185 `_) 6.7 (2025-02-07) ================ - Drop support for Python 3.8. - Add option ``--only-level=level`` to run tests only at the specified level. (`#188 `_) 6.6.1 (2024-12-13) ================== - Make signatures in ``tb_format`` Python 3.12+ compatible (`#186 `_) 6.6 (2024-10-16) ================ - Make tests compatible with Python 3.13 + add support for that version. (`#181 `_) - Drop support for Python 3.7. 6.5 (2024-08-06) ================ - Remove setuptools fossils. - ``unittest.TestCase.subTest`` support (`#91 `_). - remove support for ``setup.py``'s ``test`` command. Support for this command has been dropped by modern ``setuptools`` versions and correspondingly has been removed from most ``zopefoundation`` packages; ``zope.testrunner`` now follows. - ``setup.py``'s ``ftest`` command is now only supported when the used ``setuptools`` version still supports ``test``. 6.4 (2024-02-27) ================ - Add PEP 420 support (implicit namespaces). (`#160 `_) 6.3.1 (2024-02-12) ================== - Fix XML tests when running in distribution resp. separately. (`#163 `_) 6.3 (2024-02-07) ================ - Exit cleanly when using the test runner ``--version`` argument. (`#102 `_) - Add new ``--xml `` option to write JUnit-like XML reports. Code comes from ``collective.xmltestreport``, but be aware that here ``--xml`` is not a boolean, but expects a path! (`#148 `_). - Add support for Python 3.13a3. 6.2.1 (2023-12-22) ================== - Work around Python 3.12.1+ no longer calling ``startTest`` for skipped tests (`#157 `_). 6.2 (2023-11-08) ================ - Add support for Python 3.12. - Update code and tests to ``python-subunit >= 1.4.3`` thus requiring at least this version. 6.1 (2023-08-26) ================ - Add preliminary support for Python 3.12b4. (`#149 `_) 6.0 (2023-03-28) ================ - Drop support for Python 2.7, 3.5, 3.6. 5.6 (2022-12-09) ================ - Add support for Python 3.11. - Inline a small part of ``random.Random.shuffle`` which was deprecated in Python 3.9 and removed in 3.11 (`#119 `_). - Don't trigger post mortem debugger for skipped tests. ( `#141 `_). 5.5.1 (2022-09-07) ================== - Fix: let ``--at-level=level`` with ``level <= 0`` run the tests at all levels (rather than at no level) `#138 `_. 5.5 (2022-06-24) ================ - Use ``sys._current_frames`` (rather than ``threading.enumerate``) as base for new thread detection, fixes `#130 `_. - New option ``--gc-after-test``. It calls for a garbage collection after each test and can be used to track down ``ResourceWarning``s and cyclic garbage. With ``rv = gc.collect()``, ``!`` is output on verbosity level 1 when ``rv`` is non zero (i.e. when cyclic structures have been released), ``[``*rv*``]`` on higher verbosity levels and a detailed cyclic garbage analysis on verbosity level 4+. For details, see `#133 `_. - Allow the filename for the logging configuration to be specified via the envvar ``ZOPE_TESTRUNNER_LOG_INI``. If not defined, the configuration continues to be locked for in file ``log.ini`` of the current working directory. Remember the logging configuration file in envvar ``ZOPE_TESTRUNNER_LOG_INI`` to allow spawned child processes to recreate the logging configuration. For details, see `#134 `_. 5.4.0 (2021-11-19) ================== - Improve ``--help`` documentation for ``--package-path`` option (`#121 `_). - Do not disable existing loggers during logsupport initialization (`#120 `_). - Fix tests with testtools >= 2.5.0 (`#125 `_). - Add support for Python 3.10. 5.3.0 (2021-03-17) ================== - Add support for Python 3.9. - Fix `package init file missing` warning (`#112 `_). - Make standard streams provide a `buffer` attribute on Python 3 when using `--buffer` or testing under subunit. 5.2 (2020-06-29) ================ - Add support for Python 3.8. - When a layer is run in a subprocess, read its stderr in a thread to avoid a deadlock if its stderr output (containing failing and erroring test IDs) overflows the capacity of a pipe (`#105 `_). 5.1 (2019-10-19) ================ - Recover more gracefully when layer setUp or tearDown fails, producing useful subunit output. - Prevent a spurious warning from the ``--require-unique`` option if the ``--module`` option was not used. - Add optional buffering of standard output and standard error during tests, requested via the ``--buffer`` option or enabled by default for subunit. - Fix incorrect failure counts in per-layer summary output, broken in 4.0.1. 5.0 (2019-03-19) ================ - Fix test failures and deprecation warnings occurring when using Python 3.8a1. (`#89 `_) - Drop support for Python 3.4. 4.9.2 (2018-11-24) ================== - Fix ``TypeError: a bytes-like object is required, not 'str'`` running tests in parallel on Python 3. See `issue 80 `_. 4.9.1 (2018-11-21) ================== - Fix AssertionError in _DummyThread.isAlive on Python 3 (`#81 `_). 4.9 (2018-10-05) ================ - Drop support for Python 3.3. - Add support for Python 3.7. - Enable test coverage reporting on coveralls.io and in tox.ini. - Host documentation at https://zopetestrunner.readthedocs.io - Remove untested support for the ``--pychecker`` option. See `issue 63 `_. - Update the command line interface to use ``argparse`` instead of ``optparse``. See `issue 61 `_. - Use ipdb instead of pdb for post-mortem debugging if available (`#10 `_). - Add a --require-unique option to check for duplicate test IDs. See `LP #682771 `_. - Reintroduce optional support for ``subunit``, now with support for both version 1 and version 2 of its protocol. - Handle string in exception values when formatting chained exceptions. (`#74 `_) 4.8.1 (2017-11-12) ================== - Enable ``DeprecationWarning`` earlier, when discovering test modules. This lets warnings that are raised on import (such as those produced by ``zope.deprecation.moved``) be reported. See `issue 57 `_. 4.8.0 (2017-11-10) ================== - Automatically enable ``DeprecationWarning`` when running tests. This is recommended by the Python core developers and matches the behaviour of the ``unittest`` module. This can be overridden with Python command-line options (``-W``) or environment variables (``PYTHONWARNINGS``). See `issue 54 `_. 4.7.0 (2017-05-30) ================== - Drop all support for ``subunit``. 4.6.0 (2016-12-28) ================== - Make the ``subunit`` support purely optional: applications which have been getting the dependencies via ``zope.testrunner`` should either add ``zope.testrunner[subunit]`` to their ``install_requires`` or else depend directly on ``python-subunit``. - New option ``--ignore-new-thread=`` to suppress "New thread(s)" warnings. - Support Python 3.6. 4.5.1 (2016-06-20) ================== - Fixed: Using the ``-j`` option to run tests in multiple processes caused tests that used the ``multiprocessing`` package to hang (because the testrunner replaced ``sys.stdin`` with an unclosable object). - Drop conditional dependency on ``unittest2`` (redundant after dropping support for Python 2.6). 4.5.0 (2016-05-02) ================== - Stop tests for all layers when test fails/errors when started with -x/--stop-on-error (`#37 `_). - Drop support for Python 2.6 and 3.2. 4.4.10 (2015-11-10) =================== - Add support for Python 3.5 (`#31 `_). - Insert extra paths (from ``--path``) to the front of sys.argv (`#32 `_). 4.4.9 (2015-05-21) ================== - When using ``-j``, parallelize all the tests, including the first test layer (`#28 `_). 4.4.8 (2015-05-01) ================== - Support skipped tests in subunit output (`#25 `_). - More efficient test filtering (`#26 `_). 4.4.7 (2015-04-02) ================== - Work around a bug in PyPy3's curses module (`#24 `_). 4.4.6 (2015-01-21) ================== - Restore support for instance-based test layers that regressed in 4.4.5 (`#20 `_). 4.4.5 (2015-01-06) ================== - Sort related layers close to each other to reduce the number of unnecessary teardowns (fixes `#14 `_). - Run the unit test layer first (fixes `LP #497871 `__). 4.4.4 (2014-12-27) ================== - When looking for the right location of test code, start with longest location paths first. This fixes problems with nested code locations. 4.4.3 (2014-03-19) ================== - Added support for Python 3.4. 4.4.2 (2014-02-22) ================== - Drop support for Python 3.1. - Fix post-mortem debugging when a non-printable exception happens (https://github.com/zopefoundation/zope.testrunner/issues/8). 4.4.1 (2013-07-10) ================== - Updated ``boostrap.py`` to version 2.2. - Fix nondeterministic test failures on Python 3.3 - Tear down layers after ``post_mortem`` debugging is finished. - Fix tests that write to source directory, it might be read-only. 4.4.0 (2013-06-06) ================== - Fix tests selection when the negative "!" pattern is used several times (LP #1160965) - Moved tests into a 'tests' subpackage. - Made ``python -m zope.testrunner`` work again. - Support 'skip' feature of unittest2 (which became the new unittest in Python 2.7). - Better diagnostics when communication with subprocess fails (https://github.com/zopefoundation/zope.testrunner/issues/5). - Do not break subprocess execution when the test suite changes the working directory (https://github.com/zopefoundation/zope.testrunner/issues/6). - Count test module import errors as errors (LP #1026576). 4.3.3 (2013-03-03) ================== - Running layers in sub-processes did not use to work when run via ``python setup.py ftest`` since it tried to run setup.py with all the command line options. It now detects ``setup.py`` runs and we run the test runner directly. 4.3.2 (2013-03-03) ================== - Fix ``SkipLayers`` class in cases where the distribution specifies a ``test_suite`` value. 4.3.1 (2013-03-02) ================== - Fixed a bug in the `ftest` command and added a test. - Fixed a trivial test failure with Python 3 of the previous release. 4.3.0 (2013-03-02) ================== - Expose `ftest` distutils command via an entry point. - Added tests for ``zope.testrunner.eggsupport``. 4.2.0 (2013-02-12) ================== - Dropped use of 2to3, rewrote source code to be compatible with all Python versions. Introduced a dependency on `six`_. 4.1.1 (2013-02-08) ================== - Dropped use of zope.fixers (LP: #1118877). - Fixed tox test error reporting; fixed tests on Pythons 2.6, 3.1, 3.2, 3.3 and PyPy 1.9. - Fix --shuffle ordering on Python 3.2 to be the same as it was on older Python versions. - Fix --shuffle nondeterminism when multiple test layers are present. Note: this will likely change the order of tests for the same --shuffle-seed. - New option: --profile-directory. Use it in the test suite so that tests executed by detox in parallel don't conflict. - Use a temporary coverage directory in the test suite so that tests executed by detox in parallel don't conflict. - Fix --post-mortem (aka -D, --pdb) when a test module cannot be imported or is invalid (LP #1119363). 4.1.0 (2013-02-07) ================== - Replaced deprecated ``zope.interface.implements`` usage with equivalent ``zope.interface.implementer`` decorator. - Dropped support for Python 2.4 and 2.5. - Made StartUpFailure compatible with unittest.TextTestRunner() (LP #1118344). 4.0.4 (2011-10-25) ================== - Work around sporadic timing-related issues in the subprocess buffering tests. Thanks to Jonathan Ballet for the patch! 4.0.3 (2011-03-17) ================== - Added back support for Python <= 2.6 which was broken in 4.0.2. 4.0.2 (2011-03-16) ================== - Added back Python 3 support which was broken in 4.0.1. - Fixed `Unexpected success`_ support by implementing the whole concept. - Added support for the new __pycache__ directories in Python 3.2. 4.0.1 (2011-02-21) ================== - LP #719369: An `Unexpected success`_ (concept introduced in Python 2.7) is no longer handled as success but as failure. This is a workaround. The whole unexpected success concept might be implemented later. .. _`Unexpected success`: http://www.voidspace.org.uk/python/articles/unittest2.shtml#more-skipping 4.0.0 (2010-10-19) ================== - Show more information about layers whose setup fails (LP #638153). 4.0.0b5 (2010-07-20) ==================== - Update fix for LP #221151 to a spelling compatible with Python 2.4. - Timestamps are now always included in subunit output (r114849). - LP #591309: fix a crash when subunit reports test failures containing UTF8-encoded data. 4.0.0b4 (2010-06-23) ==================== - Package as a zipfile to work around Python 2.4 distutils bug (no feature changes or bugfixes in ``zope.testrunner`` itself). 4.0.0b3 (2010-06-16) ==================== - LP #221151: keep ``unittest.TestCase.shortDescription`` happy by supplying a ``_testMethodDoc`` attribute. - LP #595052: keep the distribution installable under Python 2.4: its distutils appears to munge the empty ``__init__.py`` file in the ``foo.bar`` egg used for testing into a directory. - LP #580083: fix the ``bin/test`` script to run only tests from ``zope.testrunner``. - LP #579019: When layers were run in parallel, their tearDown was not called. Additionally, the first layer which was run in the main thread did not have its tearDown called either. 4.0.0b2 (2010-05-03) ==================== - Having 'sampletests' in the MANIFEST.in gave warnings, but doesn't actually seem to include any more files, so I removed it. - Moved zope.testing.exceptions to zope.testrunner.exceptions. Now zope.testrunner no longer requires zope.testing except for when running its own tests. 4.0.0b1 (2010-04-29) ==================== - Initial release of the testrunner from zope.testrunner as its own module. (Previously it was part of zope.testing.) .. _six: http://pypi.python.org/pypi/six ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/README.rst0000644000076600000240000000212715011314374016024 0ustar00m.howitzstaff================= zope.testrunner ================= .. image:: https://img.shields.io/pypi/v/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Supported Python versions .. image:: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.testrunner/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.testrunner?branch=master .. image:: https://readthedocs.org/projects/zopetestrunner/badge/?version=latest :target: https://zopetestrunner.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. contents:: This package provides a flexible test runner with layer support. Detailed documentation is hosted at https://zopetestrunner.readthedocs.io ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/buildout.cfg0000644000076600000240000000031315011314374016640 0ustar00m.howitzstaff[buildout] develop = . parts = test python [test] recipe = zc.recipe.testrunner eggs = zope.testrunner[test] [python] recipe = zc.recipe.egg eggs = ${test:eggs} scripts = nothanks interpreter = python ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8462698 zope_testrunner-7.3/docs/0000755000076600000240000000000015011314376015265 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/api.rst0000644000076600000240000000017615011314374016572 0ustar00m.howitzstaff=============== API Reference =============== .. automodule:: zope.testrunner .. autoclass:: zope.testrunner.runner.Runner ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/changelog.rst0000644000076600000240000000003415011314374017741 0ustar00m.howitzstaff.. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/cli.rst0000644000076600000240000000042215011314374016562 0ustar00m.howitzstaff==================== Command Line Usage ==================== This package provides a script, ``zope-testrunner``, that's available when installed via pip. It can also be used as a Python module with the ``-m`` option. .. command-output:: python -m zope.testrunner --help ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/conf.py0000644000076600000240000002147015011314374016566 0ustar00m.howitzstaff#!/usr/bin/env python3 # # zope.testrunner documentation build configuration file, created by # sphinx-quickstart on Thu Jan 29 11:31:12 2015. # # 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 os import sys from importlib.metadata import distribution sys.path.append(os.path.abspath('../src')) rqmt = distribution('zope.testrunner') # 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', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'sphinxcontrib.programoutput', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = {'.rst': 'restructuredtext'} # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'zope.testrunner' copyright = '2015-2017, Zope Foundation 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 = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2])) # The full version, including alpha/beta/rc tags. release = rqmt.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 = 'obj' # 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 = 'zopetestrunnerdoc' # -- 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', 'zopetestrunner.tex', 'zope.testrunner Documentation', 'Zope Foundation 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 = [ ('index', 'zopetestrunner', 'zope.testrunner Documentation', ['Zope Foundation and Contributors'], 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', 'zopetestrunner', 'zope.testrunner Documentation', 'Zope Foundation and Contributors', 'zopetestrunner', '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 # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('https://docs.python.org/', None), } autodoc_default_flags = ['members', 'show-inheritance'] autoclass_content = 'both' autodoc_member_order = 'bysource' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/getting-started.rst0000644000076600000240000001027715011314374021131 0ustar00m.howitzstaff======================= Using zope.testrunner ======================= .. IMPORTANT: This document is included in the long_description for rendering on PyPI, so it cannot use Sphinx-only features. Installation ============ Buildout-based projects ----------------------- zope.testrunner is often used for projects that use buildout_:: [buildout] develop = . parts = ... test ... [test] recipe = zc.recipe.testrunner eggs = mypackage The usual buildout process :: python bootstrap.py bin/buildout creates a ``bin/test`` script that will run the tests for *mypackage*. .. tip:: zc.recipe.testrunner_ takes care to specify the right ``--test-path`` option in the generated script. You can add other options (such as ``--tests-pattern``) too; check zc.recipe.testrunner_'s documentation for details. Virtualenv-based projects ------------------------- ``pip install zope.testrunner`` and you'll get a ``zope-testrunner`` script. Run your tests with :: zope-testrunner --test-path=path/to/your/source/tree Your source code needs to be available for the testrunner to import, so you need to run ``python setup.py install`` or ``pip install -e .`` into the same virtualenv_. Some useful command-line options to get you started =================================================== -p show a progress indicator -v increase verbosity -c colorize the output -t test specify test names (one or more regexes) -m module specify test modules (one or more regexes) -s package specify test packages (one or more regexes) --list-tests show names of tests instead of running them -x stop on first error or failure -D, --pdb enable post-mortem debugging of test failures --xml path generate XML reports to be written at the given path --help show *all* command-line options (there are many more!) For example :: bin/test -pvc -m test_foo -t TestBar runs all TestBar tests from a module called test_foo.py. Writing tests ============= ``zope.testrunner`` expects to find your tests inside your package directory, in a subpackage or module named ``tests``. Test modules in a test subpackage should be named ``test*.py``. .. tip:: You can change these assumptions with ``--tests-pattern`` and ``--test-file-pattern`` test runner options. Tests themselves should be classes inheriting from ``unittest.TestCase``, and if you wish to use doctests, please tell the test runner where to find them and what options to use for them in by supplying a function named ``test_suite``. Example:: import unittest import doctest class TestArithmetic(unittest.TestCase): def test_two_plus_two(self): self.assertEqual(2 + 2, 4) def doctest_string_formatting(): """Test Python string formatting >>> print('{} + {}'.format(2, 2)) 2 + 2 """ def test_suite(): return unittest.TestSuite([ unittest.defaultTestLoader.loadTestsFromName(__name__), doctest.DocTestSuite(), doctest.DocFileSuite('../README.txt', optionflags=doctest.ELLIPSIS), ]) Test grouping ============= In addition to per-package and per-module filtering, zope.testrunner has other mechanisms for grouping tests: * **layers** allow you to have shared setup/teardown code to be used by a group of tests, that is executed only once, and not for each test. Layers are orthogonal to the usual package/module structure and are specified by setting the ``layer`` attribute on test suites. * **levels** allow you to group slow-running tests and not run them by default. They're specified by setting the ``level`` attribute on test suites to an int. Other features ============== zope.testrunner can profile your tests, measure test coverage, check for memory leaks, integrate with subunit_, shuffle the test execution order, and run multiple tests in parallel. .. _buildout: https://buildout.readthedocs.io .. _virtualenv: https://virtualenv.pypa.io/ .. _zc.recipe.testrunner: https://pypi.python.org/pypi/zc.recipe.testrunner .. _subunit: https://pypi.python.org/pypi/python-subunit ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/index.rst0000644000076600000240000000373415011314374017133 0ustar00m.howitzstaff======================================== zope.testrunner Detailed Documentation ======================================== .. image:: https://img.shields.io/pypi/v/zope.testrunner.svg :target: https://pypi.python.org/pypi/zope.testrunner/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Supported Python versions .. image:: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.testrunner/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.testrunner?branch=master For an overview of features, see :doc:`testrunner`. To get started testing right away, see :doc:`getting-started` .. toctree:: :maxdepth: 2 :caption: Usage testrunner getting-started cli .. toctree:: :maxdepth: 2 :caption: Writing Tests testrunner-layers-api testrunner-layers-ntd testrunner-layers-instances .. toctree:: :maxdepth: 2 :caption: Running Tests testrunner-discovery testrunner-layers testrunner-test-selection testrunner-shuffle testrunner-debugging testrunner-coverage testrunner-profiling testrunner-wo-source testrunner-repeat testrunner-gc testrunner-leaks testrunner-new-threads .. toctree:: :maxdepth: 2 :caption: Output Control testrunner-verbose testrunner-progress testrunner-colors testrunner-errors .. toctree:: :maxdepth: 2 :caption: Advanced testrunner-knit testrunner-edge-cases .. toctree:: :maxdepth: 2 :caption: zope.testrunner API testrunner-simple testrunner-arguments api .. toctree:: :maxdepth: 2 changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/requirements.txt0000644000076600000240000000006315011314374020546 0ustar00m.howitzstaffSphinx sphinxcontrib.programoutput zope.testrunner ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-arguments.rst0000644000076600000240000000010315011314374022223 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-arguments.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-colors.rst0000644000076600000240000000010015011314374021514 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-colors.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-coverage.rst0000644000076600000240000000010215011314374022010 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-coverage.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-debugging.rst0000644000076600000240000000010315011314374022151 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-debugging.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-discovery.rst0000644000076600000240000000010315011314374022225 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-discovery.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-edge-cases.rst0000644000076600000240000000010415011314374022217 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-edge-cases.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-errors.rst0000644000076600000240000000010015011314374021527 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-errors.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-gc.rst0000644000076600000240000000007415011314374020616 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-gc.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-knit.rst0000644000076600000240000000007615011314374021174 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-knit.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-layers-api.rst0000644000076600000240000000010415011314374022265 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-layers-api.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-layers-instances.rst0000644000076600000240000000011215011314374023502 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-layers-instances.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-layers-ntd.rst0000644000076600000240000000010415011314374022301 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-layers-ntd.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-layers.rst0000644000076600000240000000010015011314374021512 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-layers.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-leaks.rst0000644000076600000240000000007715011314374021327 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-leaks.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-new-threads.rst0000644000076600000240000000010515011314374022441 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-new-threads.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-profiling.rst0000644000076600000240000000010315011314374022207 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-profiling.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-progress.rst0000644000076600000240000000010215011314374022061 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-progress.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-repeat.rst0000644000076600000240000000010015011314374021473 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-repeat.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-shuffle.rst0000644000076600000240000000010115011314374021650 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-shuffle.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-simple.rst0000644000076600000240000000010015011314374021504 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-simple.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-test-selection.rst0000644000076600000240000000011015011314374023156 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-test-selection.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-verbose.rst0000644000076600000240000000010115011314374021661 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-verbose.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner-wo-source.rst0000644000076600000240000000010315011314374022141 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner-wo-source.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/docs/testrunner.rst0000644000076600000240000000007115011314374020224 0ustar00m.howitzstaff.. include:: ../src/zope/testrunner/tests/testrunner.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/pyproject.toml0000644000076600000240000000112215011314374017243 0ustar00m.howitzstaff# # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python [build-system] requires = ["setuptools <= 75.6.0"] build-backend = "setuptools.build_meta" [tool.coverage.run] branch = true source = ["zope.testrunner"] [tool.coverage.report] fail_under = 84.4 precision = 2 ignore_errors = true show_missing = true exclude_lines = ["pragma: no cover", "pragma: nocover", "except ImportError:", "raise NotImplementedError", "if __name__ == '__main__':", "self.fail", "raise AssertionError", "raise unittest.Skip"] [tool.coverage.html] directory = "parts/htmlcov" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8770053 zope_testrunner-7.3/setup.cfg0000644000076600000240000000132515011314376016157 0ustar00m.howitzstaff[flake8] doctests = 1 builtins = layerx per-file-ignores = src/zope/testrunner/tests/test_runner.py: E701 src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_ntds.py: E702 src/zope/testrunner/tests/testrunner-ex/sample3/sampletests_d.py: E702 src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_1.py: F821 [check-manifest] ignore = .editorconfig .meta.toml docs/_build/html/_sources/* [isort] force_single_line = True combine_as_imports = True sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER known_third_party = docutils, pkg_resources, pytz known_zope = known_first_party = default_section = ZOPE line_length = 79 lines_after_imports = 2 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/setup.py0000644000076600000240000000616715011314374016057 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004, 2013 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## import os from setuptools import setup version = '7.3' INSTALL_REQUIRES = [ 'setuptools', 'zope.exceptions', 'zope.interface', ] TESTS_REQUIRE = [ 'zope.testing', ] EXTRAS_REQUIRE = { 'test': TESTS_REQUIRE, 'subunit': [ 'python-subunit >= 1.4.3', 'testtools >= 0.9.30', ], 'docs': [ 'Sphinx', 'sphinxcontrib-programoutput', ], } def read(*names): with open(os.path.join(*names)) as f: return f.read() long_description = ( read('README.rst') + '\n\n' + read("docs", 'getting-started.rst') + '\n\n' + read('CHANGES.rst') ) setup( name='zope.testrunner', version=version, url='https://github.com/zopefoundation/zope.testrunner', license='ZPL-2.1', description='Zope testrunner script.', long_description=long_description, author='Zope Foundation and Contributors', author_email='zope-dev@zope.dev', packages=[ "zope", "zope.testrunner", ], package_dir={'': 'src'}, classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Framework :: Zope :: 3", "Intended Audience :: Developers", "License :: OSI Approved :: Zope Public License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Testing", ], namespace_packages=['zope'], python_requires='>=3.9', install_requires=INSTALL_REQUIRES, extras_require=EXTRAS_REQUIRE, entry_points={ 'console_scripts': ['zope-testrunner = zope.testrunner:run'], }, include_package_data=True, zip_safe=False, ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8363533 zope_testrunner-7.3/src/0000755000076600000240000000000015011314376015124 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8463883 zope_testrunner-7.3/src/zope/0000755000076600000240000000000015011314376016101 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/__init__.py0000644000076600000240000000007015011314374020205 0ustar00m.howitzstaff__import__('pkg_resources').declare_namespace(__name__) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8517778 zope_testrunner-7.3/src/zope/testrunner/0000755000076600000240000000000015011314376020312 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/__init__.py0000644000076600000240000000613115011314374022422 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2013 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test runner """ import os import sys import pkg_resources # noqa: F401 unused import, see GitHub issue #194 def run(defaults=None, args=None, script_parts=None, cwd=None, warnings=None): """Main runner function which can be and is being used from main programs. Will execute the tests and exit the process according to the test result. .. versionchanged:: 4.8.0 Add the *warnings* keyword argument. See :class:`zope.testrunner.runner.Runner` """ failed = run_internal(defaults, args, script_parts=script_parts, cwd=cwd, warnings=warnings) sys.exit(int(failed)) def run_internal(defaults=None, args=None, script_parts=None, cwd=None, warnings=None): """Execute tests. Returns whether errors or failures occured during testing. .. versionchanged:: 4.8.0 Add the *warnings* keyword argument. See :class:`zope.testrunner.runner.Runner` """ if script_parts is None: script_parts = _script_parts(args) if cwd is None: cwd = os.getcwd() # XXX Bah. Lazy import to avoid circular/early import problems from zope.testrunner.runner import Runner runner = Runner( defaults, args, script_parts=script_parts, cwd=cwd, warnings=warnings) runner.run() return runner.failed def _script_parts(args=None): script_parts = (args or sys.argv)[0:1] # If we are running via setup.py, then we'll have to run the # sub-process differently. if script_parts[0] == 'setup.py': script_parts = ['-c', 'from zope.testrunner import run; run()'] else: # make sure we remember the absolute path early -- the tests might # do an os.chdir() script_parts[0] = os.path.abspath(script_parts[0]) if not os.path.exists(script_parts[0]): # uhh, no wrapper script? this happens on Windows sometimes, # where there are like three wrapper scripts with various suffixes, # and I don't want to go looking up what they might be. script_parts = ['-m', 'zope.testrunner'] return script_parts if __name__ == '__main__': # this used to allow people to try out the test runner with # python -m zope.testrunner --test-path . # on Python 2.5. This broke on 2.6, and 2.7 and newer use __main__.py # for that. But there are some users out there who actually use # python -e zope.testrunner.__init__ --test-path . # so let's keep this for BBB run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/__main__.py0000644000076600000240000000144715011314374022410 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2013 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## if __name__ == '__main__': # allow people to try out the test runner with # python -m zope.testrunner --test-path . from zope.testrunner import run run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/_doctest.py0000644000076600000240000000377715011314374022504 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Doc test support for the test runner. """ import doctest import sys import zope.testrunner.feature from zope.testrunner.exceptions import DocTestFailureException class DocTest(zope.testrunner.feature.Feature): active = True def global_setup(self): options = self.runner.options output = options.output self.old_reporting_flags = doctest.set_unittest_reportflags(0) reporting_flags = 0 if options.ndiff: reporting_flags = doctest.REPORT_NDIFF if options.udiff: if reporting_flags: output.error( "Can only give one of --ndiff, --udiff, or --cdiff") sys.exit(1) reporting_flags = doctest.REPORT_UDIFF if options.cdiff: if reporting_flags: output.error( "Can only give one of --ndiff, --udiff, or --cdiff") sys.exit(1) reporting_flags = doctest.REPORT_CDIFF if options.report_only_first_failure: reporting_flags |= doctest.REPORT_ONLY_FIRST_FAILURE if reporting_flags: doctest.set_unittest_reportflags(reporting_flags) def global_shutdown(self): doctest.set_unittest_reportflags(self.old_reporting_flags) # Use a special exception for the test runner. doctest.DocTestCase.failureException = DocTestFailureException ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/coverage.py0000644000076600000240000001147315011314374022463 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Code coverage analysis """ import os.path import sys import threading import trace import zope.testrunner.feature from zope.testrunner.find import test_dirs # For some reason, the doctest module resets the trace callable randomly, thus # disabling the coverage. Simply disallow the code from doing this. A real # trace can be set, so that debugging still works. osettrace = sys.settrace def settrace(trace): if trace is None: # pragma: no cover return osettrace(trace) class TestTrace(trace.Trace): """Simple tracer. >>> tracer = TestTrace([], count=False, trace=False) Simple rules for use: you can't stop the tracer if it not started and you can't start the tracer if it already started: >>> tracer.stop() Traceback (most recent call last): File 'testrunner.py' AssertionError: can't stop if not started >>> tracer.start() >>> tracer.start() Traceback (most recent call last): File 'testrunner.py' AssertionError: can't start if already started >>> tracer.stop() >>> tracer.stop() Traceback (most recent call last): File 'testrunner.py' AssertionError: can't stop if not started """ def __init__(self, directories, **kw): trace.Trace.__init__(self, **kw) self.ignore = TestIgnore(directories) self.started = False def start(self): assert not self.started, "can't start if already started" if not self.donothing: sys.settrace = settrace sys.settrace(self.globaltrace) threading.settrace(self.globaltrace) self.started = True def stop(self): assert self.started, "can't stop if not started" if not self.donothing: sys.settrace = osettrace sys.settrace(None) threading.settrace(None) self.started = False class TestIgnore: def __init__(self, directories): self._test_dirs = [self._filenameFormat(d[0]) + os.path.sep for d in directories] self._ignore = {} self._ignored = self._ignore.get def names(self, filename, modulename): # Special case: Modules generated from text files; i.e. doctests if modulename == '': return True filename = self._filenameFormat(filename) ignore = self._ignored(filename) if ignore is None: ignore = True if filename is not None: for d in self._test_dirs: if filename.startswith(d): ignore = False break self._ignore[filename] = ignore return ignore def _filenameFormat(self, filename): return os.path.abspath(filename) if sys.platform == 'win32': # on win32 drive name can be passed with different case to `names` # that lets e.g. the coverage profiler skip complete files # _filenameFormat will make sure that all drive and filenames get # lowercased albeit trace coverage has still problems with lowercase # drive letters when determining the dotted module name OldTestIgnore = TestIgnore class TestIgnore(OldTestIgnore): def _filenameFormat(self, filename): return os.path.normcase(os.path.abspath(filename)) class Coverage(zope.testrunner.feature.Feature): tracer = None directory = None def __init__(self, runner): super().__init__(runner) self.active = bool(runner.options.coverage) def global_setup(self): """Executed once when the test runner is being set up.""" self.directory = os.path.join( os.getcwd(), self.runner.options.coverage) # FIXME: This shouldn't rely on the find feature directly. self.tracer = TestTrace(test_dirs(self.runner.options, {}), trace=False, count=True) self.tracer.start() def early_teardown(self): """Executed once directly after all tests.""" self.tracer.stop() def report(self): """Executed once after all tests have been run and all setup was torn down.""" r = self.tracer.results() r.write_results(summary=True, coverdir=self.directory) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/debug.py0000644000076600000240000000540715011314374021756 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Debug functions """ import doctest import io import pdb # noqa: T100 import for pdb found import sys import threading import traceback try: import ipdb # noqa: T100 import for ipdb found _ipdb_state = threading.local() except ImportError: ipdb = None import zope.testrunner.interfaces def _use_ipdb(): """Check whether ipdb is usable.""" global _ipdb_ok if ipdb is None: return False if getattr(_ipdb_state, 'ok', None) is None: _ipdb_state.ok = False if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): try: sys.stdin.fileno() except (AttributeError, io.UnsupportedOperation): pass else: _ipdb_state.ok = True return _ipdb_state.ok def post_mortem(exc_info): err = exc_info[1] if isinstance(err, (doctest.UnexpectedException, doctest.DocTestFailure)): if isinstance(err, doctest.UnexpectedException): exc_info = err.exc_info # Print out location info if the error was in a doctest if exc_info[2].tb_frame.f_code.co_filename == '': print_doctest_location(err) else: print_doctest_location(err) # Hm, we have a DocTestFailure exception. We need to # generate our own traceback try: exec(('raise ValueError' '("Expected and actual output are different")' ), err.test.globs) except BaseException: exc_info = sys.exc_info() print(''.join(traceback.format_exception_only(exc_info[0], exc_info[1]))) if _use_ipdb(): ipdb.post_mortem(exc_info[2]) else: pdb.post_mortem(exc_info[2]) raise zope.testrunner.interfaces.EndRun() def print_doctest_location(err): # This mimics pdb's output, which gives way cool results in emacs :) filename = err.test.filename if filename.endswith('.pyc'): filename = filename[:-1] print( "> {}({})_()".format( filename, err.test.lineno + err.example.lineno + 1)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/digraph.py0000644000076600000240000001644115011314374022306 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2022 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Directed graph """ from itertools import count class DiGraph: """Directed graph. A directed graph is a set of nodes together with a neighboring relation. The class makes intensive use of dicts; therefore, hashability is important. Therefore, the class usually does not work with the nodes directly but transforms them via a ``make_hashable`` function, ``id`` by default. This works well for object types where equality is identity. For other types, you may need to deactive the transformation or use a different ``make_hashable``. """ def __init__(self, nodes=None, make_hashable=id): self._nodes = set() # transformed nodes self._neighbors = {} # node --> neighbors -- transformed if make_hashable: tr2n = {} # transform -> node tr_node = make_hashable def utr_node(node): return tr2n[node] def tr_nodes(nodes): ns = set() add = ns.add for n in nodes: trn = make_hashable(n) if trn not in tr2n: tr2n[trn] = n add(trn) return ns else: def tr_nodes(nodes): return set(nodes) # noqa: E731 utr_node = tr_node = lambda node: node self._transform_node = tr_node self._transform_nodes = tr_nodes self._untransform_node = utr_node if nodes is not None: self.add_nodes(nodes) def add_nodes(self, nodes): """add *nodes* (iterator) to the graph's nodes.""" self._nodes |= self._transform_nodes(nodes) def add_neighbors(self, node, neighbors, ignore_unknown=True): """add *neighbors* (iterator) as neighbors for *node*. if *ignore_unknown*, unknown nodes in *neighbors* are ignored, otherwise a ``KeyError`` is raised. """ tr_n = self._transform_node(node) nodes = self._nodes nbad = tr_n not in nodes if nbad: if ignore_unknown: return else: raise KeyError(node) tr_neighbors = self._transform_nodes(neighbors) known_neighbors = tr_neighbors & nodes if not ignore_unknown and len(known_neighbors) != len(tr_neighbors): raise KeyError(tr_neighbors - known_neighbors) nbs = self._neighbors.get(tr_n) if nbs is None: self._neighbors[tr_n] = known_neighbors else: nbs |= known_neighbors def nodes(self): """iterate of the graph's nodes.""" utr = self._untransform_node for n in self._nodes: yield utr(n) def neighbors(self, node): """iterate over *node*'s neighbors.""" utr = self._untransform_node for n in self._neighbors.get(self._transform_node(node), ()): yield utr(n) def sccs(self, trivial=False): """iteratate over the strongly connected components. If *trivial*, include the trivial components; otherwise only the cycles. This is an implementation of the "Tarjan SCC" algorithm. """ # any node is either in ``unvisited`` or in ``state`` unvisited = self._nodes.copy() state = {} # nodes -> state ancestors = [] # the ancestors of the currently processed node stack = [] # the nodes which might still be on a cycle # the algorithm visits each node twice in a depth first order # In the first visit, visits for the unprocessed neighbors # are scheduled as well as the second visit to this # node after all neighbors have been processed. dfs = count() # depth first search visit order rtn_marker = object() # marks second visit to ``ancestor`` top visits = [] # scheduled visits while unvisited: node = next(iter(unvisited)) # determine the depth first spanning tree rooted in *node* visits.append(node) while visits: visit = visits[-1] # ``rtn_marker`` or node if visit is rtn_marker: # returned to the top of ``ancestors`` visits.pop() node = ancestors.pop() # returned to *node* nstate = state[node] if nstate.low == nstate.dfs: # SCC root scc = [] while True: n = stack.pop() state[n].stacked = False scc.append(n) if n is node: break if len(scc) == 1 and not trivial: # check for triviality n = scc[0] if n not in self._neighbors[n]: continue # tivial -- ignore utr = self._untransform_node yield [utr(n) for n in scc] if not ancestors: # dfs tree determined assert not visits break pstate = state[ancestors[-1]] nstate = state[node] low = nstate.low if low < pstate.low: pstate.low = low else: # scheduled first visit node = visit nstate = state.get(node) if nstate is not None: # we have already been visited if nstate.stacked: # update parent pstate = state[ancestors[-1]] if nstate.dfs < pstate.low: pstate.low = nstate.dfs visits.pop() continue unvisited.remove(node) nstate = state[node] = _TarjanState(dfs) ancestors.append(node) stack.append(node) nstate.stacked = True visits[-1] = rtn_marker # schedule return visit # schedule neighbor visits visits.extend(self._neighbors.get(node, ())) class _TarjanState: """representation of a node's processing state.""" __slots__ = "stacked dfs low".split() def __init__(self, dfs): self.stacked = False self.dfs = self.low = next(dfs) def __repr__(self): return "dfs=%d low=%d stacked=%s" \ % (self.dfs, self.low, self.stacked) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/exceptions.py0000644000076600000240000000142115011314374023041 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Exceptions for zope.testrunner """ class DocTestFailureException(AssertionError): """Use custom exception for doctest unit test failures""" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/feature.py0000644000076600000240000000373615011314374022326 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Generic features for the test runner. """ import zope.interface import zope.testrunner.interfaces @zope.interface.implementer(zope.testrunner.interfaces.IFeature) class Feature: """A base class implementing no-op methods for the IFeature interface.""" active = False def __init__(self, runner): self.runner = runner def global_setup(self): """Executed once when the test runner is being set up.""" pass def late_setup(self): """Executed once right before the actual tests get executed and after all global setups have happened. """ pass def layer_setup(self, layer): """Executed once after a layer was set up.""" pass def layer_teardown(self, layer): """Executed once after a layer was run.""" pass def test_setup(self): """Executed once before each test.""" pass def test_teardown(self): """Executed once after each test.""" pass def early_teardown(self): """Executed once directly after all tests.""" pass def global_teardown(self): """Executed once after all tests where run and early teardowns have happened.""" pass def report(self): """Executed once after all tests have been run and all setup was torn down.""" pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/filter.py0000644000076600000240000001060115011314374022145 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Filter which tests to run. """ import re import zope.testrunner.feature UNITTEST_LAYER = 'zope.testrunner.layer.UnitTests' class Filter(zope.testrunner.feature.Feature): """Filters and orders all tests registered until now.""" active = True def global_setup(self): layers = self.runner.tests_by_layer_name options = self.runner.options if UNITTEST_LAYER in layers: # We start out assuming unit tests should run and look for reasons # why they shouldn't be run. should_run = True if (not options.non_unit): if options.layer: accept = build_filtering_func(options.layer) should_run = accept(UNITTEST_LAYER) else: should_run = True else: should_run = False if not should_run: layers.pop(UNITTEST_LAYER) if self.runner.options.resume_layer is not None: for name in list(layers): if name != self.runner.options.resume_layer: layers.pop(name) if not layers: self.runner.options.output.error_with_banner( "Cannot find layer %s" % self.runner.options.resume_layer) self.runner.errors.append( ("subprocess failed for %s" % self.runner.options.resume_layer, None)) elif self.runner.options.layer: accept = build_filtering_func(self.runner.options.layer) for name in list(layers): if not accept(name): # No pattern matched this name so we remove it layers.pop(name) if (self.runner.options.verbose and not self.runner.options.resume_layer): if self.runner.options.all: msg = "Running tests at all levels" else: if self.runner.options.only_level is None: msg = ( "Running tests at level %d" % self.runner.options.at_level) else: msg = ( "Running tests only at level %d" % self.runner.options.only_level) self.runner.options.output.info(msg) def report(self): if not self.runner.do_run_tests: return if self.runner.options.resume_layer: return if self.runner.options.verbose: self.runner.options.output.tests_with_errors(self.runner.errors) self.runner.options.output.tests_with_failures( self.runner.failures) def build_filtering_func(patterns): """Build a filtering function from a set of patterns Patterns are understood as regular expressions, with the additional feature that, prefixed by "!", they create a "don't match" rule. This returns a function which returns True if a string matches the set of patterns, or False if it doesn't match. """ selected = [] unselected = [] for pattern in patterns: if pattern.startswith('!'): store = unselected.append pattern = pattern[1:] else: store = selected.append store(re.compile(pattern).search) if not selected and unselected: # If there's no selection patterns but some un-selection patterns, # suppose we want everything (that is, everything that matches '.'), # minus the un-selection ones. selected.append(re.compile('.').search) def accept(value): return (any(search(value) for search in selected) and not any(search(value) for search in unselected)) return accept ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/find.py0000644000076600000240000004523015011314374021606 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test discovery """ import os import re import sys import unittest import zope.testrunner.debug import zope.testrunner.feature import zope.testrunner.layer from zope.testrunner.filter import build_filtering_func identifier = re.compile(r'[_a-z]\w*$', re.I).match IGNORE_FOLDERS = { '.git', 'node_modules', '__pycache__' } class DuplicateTestIDError(Exception): """Raised whenever a test ID is encountered twice during loading.""" class StartUpFailure(unittest.TestCase): """Empty test case added to the test suite to indicate import failures. >>> class Options(object): ... post_mortem = False >>> options = Options() Normally the StartUpFailure just acts as an empty test suite to satisfy the test runner and statistics: >>> s = StartUpFailure(options, 'fauxmodule', None) >>> s >>> isinstance(s,unittest.TestCase) True >>> s.shortDescription() 'StartUpFailure: import errors in fauxmodule.' Sometimes test suited collected by zope.testrunner end up being run by a regular unittest TestRunner, and it's not hard to make sure StartUpFailure does something sensible in that case >>> r = unittest.TestResult() >>> _ = s.run(r) >>> print(r.failures[0][1].rstrip()) # doctest: +ELLIPSIS Traceback (most recent call last): ... AssertionError: could not import fauxmodule If you'd like more details, be sure to pass the original exc_info:: >>> import sys >>> try: ... raise Exception('something bad happened during import') ... except: ... exc_info = sys.exc_info() >>> s = StartUpFailure(options, 'fauxmodule', exc_info) >>> r = unittest.TestResult() >>> _ = s.run(r) >>> print(r.errors[0][0].shortDescription()) StartUpFailure: import errors in fauxmodule. >>> print(r.errors[0][1].rstrip()) # doctest: +ELLIPSIS Traceback (most recent call last): ... Exception: something bad happened during import However, if the post mortem option is enabled: >>> options.post_mortem = True ...then the the StartUpFailure will start the debugger and stop the test run after the debugger quits. To simulate the user pressing 'c' and hitting return in the debugger, we use a FakeInputContinueGenerator: >>> from zope.testrunner.runner import FakeInputContinueGenerator >>> old_stdin = sys.stdin >>> sys.stdin = FakeInputContinueGenerator() Now we can see the EndRun exception that is raised by the postmortem debugger to indicate that debugging is finished and the test run should be terminated: >>> from zope.testrunner.interfaces import EndRun >>> try: #doctest: +ELLIPSIS ... # Needed to prevent the result from starting with '...' ... print("Result:") ... StartUpFailure(options, None, exc_info) ... except EndRun: ... print("EndRun raised") ... finally: ... sys.stdin = old_stdin Result: Exception: something bad happened during import ... (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** EndRun raised Annoyingly, sometimes StartUpFailures occur when postmortem debugging is enabled but no exc_info is passed. In this case, we raise a sensible exception rather than letting the debugger barf with an AttributeError: >>> options.post_mortem = True >>> StartUpFailure(options, None, exc_info[:2]+(None,)) Traceback (most recent call last): ... TypeError: If post_mortem is specified, full exc_info must be passed! """ def __init__(self, options, module, exc_info): if options.post_mortem: for item in exc_info: if item is None: raise TypeError('If post_mortem is specified, ' 'full exc_info must be passed!') zope.testrunner.debug.post_mortem(exc_info) self.module = module self.exc_info = exc_info super().__init__() def shortDescription(self): return 'StartUpFailure: import errors in %s.' % self.module def __repr__(self): return '' % self.module def runTest(self): if self.exc_info is None or any(x is None for x in self.exc_info): self.fail("could not import %s" % self.module) else: try: _, value, tb = self.exc_info raise value.with_traceback(tb) finally: value = None tb = None def find_tests(options, found_suites=None): """Creates a dictionary mapping layer name to a suite of tests to be run in that layer. Passing a list of suites using the found_suites parameter will cause that list of suites to be used instead of attempting to load them from the filesystem. This is useful for unit testing the test runner. """ remove_stale_bytecode(options) suites = {} dupe_ids = set() test_accept = build_filtering_func(options.test) module_accept = build_filtering_func(options.module) if found_suites is None: found_suites = find_suites(options, accept=module_accept) for suite in found_suites: for test, layer_name in tests_from_suite(suite, options, accept=test_accept, duplicated_test_ids=dupe_ids): if dupe_ids: # If there are any duplicated test IDs, we stop trying to # load tests; we'll raise an error later on with all the # duplicates in it. continue suite = suites.get(layer_name) if not suite: suite = suites[layer_name] = unittest.TestSuite() suite.addTest(test) if dupe_ids: message_lines = ['Duplicate test IDs found:'] + sorted(dupe_ids) message = '\n '.join(message_lines) raise DuplicateTestIDError(message) return suites def find_suites(options, accept=None): for fpath, package in find_test_files(options): for (prefix, prefix_package) in options.prefix: if fpath.startswith(prefix) and package == prefix_package: # strip prefix, strip .py suffix and convert separator to dots noprefix = fpath[len(prefix):] noext = strip_py_ext(options, noprefix) assert noext is not None module_name = noext.replace(os.path.sep, '.') if package: module_name = package + '.' + module_name if accept is not None and not accept(module_name): continue try: module = import_name(module_name) except KeyboardInterrupt: raise except BaseException: exc_info = sys.exc_info() if not options.post_mortem: # Skip a couple of frames exc_info = ( exc_info[:2] + (exc_info[2].tb_next.tb_next,)) suite = StartUpFailure( options, module_name, exc_info) else: try: if hasattr(module, options.suite_name): suite = getattr(module, options.suite_name)() else: loader = unittest.defaultTestLoader suite = loader.loadTestsFromModule(module) if suite.countTestCases() == 0: raise TypeError( "Module %s does not define any tests" % module_name) if not isinstance(suite, unittest.TestSuite): # We extract the error message expression into a # local variable because we want the `raise` # statement to fit on a single line, to make the # testrunner-debugging-import-failure.rst doctest # see the same pdb output on Python 3.8 as on older # Python versions. bad_test_suite_msg = ( "Invalid test_suite, %r, in %s" % (suite, module_name) ) raise TypeError(bad_test_suite_msg) except KeyboardInterrupt: raise except BaseException: exc_info = sys.exc_info() if not options.post_mortem: # Suppress traceback exc_info = exc_info[:2] + (None,) suite = StartUpFailure( options, module_name, exc_info) yield suite break def find_test_files(options): found = {} for f, package in find_test_files_(options): if f not in found: found[f] = 1 yield f, package def find_test_files_(options): tests_pattern = options.tests_pattern test_file_pattern = options.test_file_pattern # If options.usecompiled, we can accept .pyc or .pyo files instead # of .py files. We'd rather use a .py file if one exists. `root2ext` # maps a test file path, sans extension, to the path with the best # extension found (.py if it exists, else .pyc or .pyo). # Note that "py" < "pyc" < "pyo", so if more than one extension is # found, the lexicographically smaller one is best. # Found a new test file, in directory `dirname`. `noext` is the # file name without an extension, and `withext` is the file name # with its extension. def update_root2ext(dirname, noext, withext): key = os.path.join(dirname, noext) new = os.path.join(dirname, withext) if key in root2ext: root2ext[key] = min(root2ext[key], new) else: root2ext[key] = new for (p, package) in test_dirs(options, {}): for dirname, dirs, files in walk_with_symlinks(options, p): root2ext = {} dirs[:] = [ d for d in dirs if identifier(d) and d not in IGNORE_FOLDERS ] d = os.path.split(dirname)[1] if tests_pattern(d) and contains_init_py(options, files): # tests directory for file in files: noext = strip_py_ext(options, file) if noext and test_file_pattern(noext): update_root2ext(dirname, noext, file) for file in files: noext = strip_py_ext(options, file) if noext and tests_pattern(noext): update_root2ext(dirname, noext, file) winners = sorted(root2ext.values()) for file in winners: yield file, package def strip_py_ext(options, path): """Return path without its .py (or .pyc or .pyo) extension, or None. If options.usecompiled is false: If path ends with ".py", the path without the extension is returned. Else None is returned. If options.usecompiled is true: If Python is running with -O, a .pyo extension is also accepted. If Python is running without -O, a .pyc extension is also accepted. """ if path.endswith(".py"): return path[:-3] if options.usecompiled: if __debug__: # Python is running without -O. ext = ".pyc" else: # Python is running with -O. ext = ".pyo" if path.endswith(ext): return path[:-len(ext)] return None def test_dirs(options, seen): if options.package: for p in options.package: p = import_name(p) for p in p.__path__: p = os.path.abspath(p) if p in seen: continue for (prefix, package) in options.prefix: if p.startswith(prefix) or p == prefix[:-1]: seen[p] = 1 yield p, package break else: yield from options.test_path def walk_with_symlinks(options, dir): # TODO -- really should have test of this that uses symlinks # this is hard on a number of levels ... for dirpath, dirs, files in os.walk(dir): dirs.sort() files.sort() dirs[:] = [d for d in dirs if d not in options.ignore_dir] yield (dirpath, dirs, files) for d in dirs: p = os.path.join(dirpath, d) if os.path.islink(p): yield from walk_with_symlinks(options, p) compiled_suffixes = '.pyc', '.pyo' def remove_stale_bytecode(options): if options.keepbytecode: return for (p, _) in options.test_path: for dirname, dirs, files in walk_with_symlinks(options, p): if '__pycache__' in dirs: # Do not recurse in there: we would end up removing all pyc # files because the main loop checks for py files in the same # directory. Besides, stale pyc files in __pycache__ are # harmless, see PEP-3147 for details (sourceless imports # work only when pyc lives in the source dir directly). dirs.remove('__pycache__') for file in files: if file[-4:] in compiled_suffixes and file[:-1] not in files: fullname = os.path.join(dirname, file) options.output.info("Removing stale bytecode file %s" % fullname) os.unlink(fullname) def contains_init_py(options, fnamelist): """Return true iff fnamelist contains a suitable spelling of __init__.py. If options.usecompiled is false, this is so iff "__init__.py" is in the list. If options.usecompiled is true, then "__init__.pyo" is also acceptable if Python is running with -O, and "__init__.pyc" is also acceptable if Python is running without -O. """ if "__init__.py" in fnamelist: return True if options.usecompiled: if __debug__: # Python is running without -O. return "__init__.pyc" in fnamelist else: # Python is running with -O. return "__init__.pyo" in fnamelist return False def import_name(name): __import__(name) return sys.modules[name] def tests_from_suite(suite, options, dlevel=1, dlayer=zope.testrunner.layer.UnitTests, accept=None, seen_test_ids=None, duplicated_test_ids=None): """Returns a sequence of (test, layer_name) The tree of suites is recursively visited, with the most specific layer taking precedence. So if a TestCase with a layer of 'foo' is contained in a TestSuite with a layer of 'bar', the test case would be returned with 'foo' as the layer. Tests are also filtered out based on the test level and accept predicate. accept is a function, returning boolean for given test name (see also build_filtering_func()). """ # We use this to track the test IDs that have been registered. # tests_from_suite will complain if it encounters the same test ID # twice. if seen_test_ids is None: seen_test_ids = set() if duplicated_test_ids is None: duplicated_test_ids = set() level = getattr(suite, 'level', dlevel) layer = getattr(suite, 'layer', dlayer) if not isinstance(layer, str): layer = name_from_layer(layer) if isinstance(suite, unittest.TestSuite): for possible_suite in suite: yield from tests_from_suite( possible_suite, options, level, layer, accept=accept, seen_test_ids=seen_test_ids, duplicated_test_ids=duplicated_test_ids) elif isinstance(suite, StartUpFailure): yield (suite, None) else: if options.require_unique_ids: suite_id = str(suite) if suite_id in seen_test_ids: duplicated_test_ids.add(suite_id) else: seen_test_ids.add(suite_id) if options.only_level is None: if options.at_level <= 0 or level <= options.at_level: if accept is None or accept(str(suite)): yield (suite, layer) else: if level == options.only_level: if accept is None or accept(str(suite)): yield (suite, layer) _layer_name_cache = {} def name_from_layer(layer): """Determine a name for the Layer using the namespace to avoid conflicts. We also cache a name -> layer mapping to enable layer_from_name to work in cases where the layer cannot be imported (such as layers defined in doctests) """ if layer.__module__ == '__builtin__': name = layer.__name__ else: name = layer.__module__ + '.' + layer.__name__ _layer_name_cache[name] = layer return name class Find(zope.testrunner.feature.Feature): """Finds tests and registers them with the test runner.""" active = True def global_setup(self): # Add directories to the path for path in reversed(self.runner.options.path): if path not in sys.path: sys.path.insert(0, path) tests = find_tests(self.runner.options, self.runner.found_suites) self.import_errors = tests.pop(None, None) self.runner.register_tests(tests) # XXX move to reporting ??? self.runner.options.output.import_errors(self.import_errors) self.runner.import_errors = list(self.import_errors or []) def report(self): self.runner.options.output.modules_with_import_problems( self.import_errors) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/formatter.py0000644000076600000240000016171515011314374022700 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Output formatting. """ import doctest import io import os import re import socket import sys import tempfile import traceback from collections.abc import MutableMapping from contextlib import contextmanager from contextlib import suppress from dataclasses import dataclass from dataclasses import field from datetime import datetime from datetime import timedelta from pathlib import Path from xml.etree import ElementTree from zope.testrunner.exceptions import DocTestFailureException from zope.testrunner.find import StartUpFailure try: import manuel.testing HAVE_MANUEL = True except ImportError: HAVE_MANUEL = False doctest_template = """ File "%s", line %s, in %s %s Want: %s Got: %s """ class OutputFormatter: """Test runner output formatter.""" # Implementation note: be careful about printing stuff to sys.stderr. # It is used for interprocess communication between the parent and the # child test runner, when you run some test layers in a subprocess. # resume_layer() reasigns sys.stderr for this reason, but be careful # and don't store the original one in __init__ or something. max_width = 80 def __init__(self, options): self.options = options self.last_width = 0 self.compute_max_width() progress = property(lambda self: self.options.progress) verbose = property(lambda self: self.options.verbose) in_subprocess = property( lambda self: ( self.options.resume_layer is not None and self.options.processes > 1)) def compute_max_width(self): """Try to determine the terminal width.""" # Note that doing this every time is more test friendly. self.max_width = tigetnum('cols', self.max_width) def getShortDescription(self, test, room): """Return a description of a test that fits in ``room`` characters.""" room -= 1 s = str(test) if len(s) > room: pos = s.find(" (") if pos >= 0: w = room - (pos + 5) if w < 1: # first portion (test method name) is too long s = s[:room - 3] + "..." else: pre = s[:pos + 2] post = s[-w:] s = f"{pre}...{post}" else: w = room - 4 s = '... ' + s[-w:] return ' ' + s[:room] def info(self, message): """Print an informative message.""" print(message) def info_suboptimal(self, message): """Print an informative message about losing some of the features. For example, when you run some tests in a subprocess, you lose the ability to use the debugger. """ print(message) def error(self, message): """Report an error.""" print(message) def error_with_banner(self, message): """Report an error with a big ASCII banner.""" print() print('*' * 70) self.error(message) print('*' * 70) print() def profiler_stats(self, stats): """Report profiler stats.""" stats.print_stats(50) def import_errors(self, import_errors): """Report test-module import errors (if any).""" if import_errors: print("Test-module import failures:") for error in import_errors: self.print_traceback("Module: %s\n" % error.module, error.exc_info), print() def tests_with_errors(self, errors): """Report names of tests with errors (if any).""" if errors: print() print("Tests with errors:") for test, exc_info in errors: print(" ", test) def tests_with_failures(self, failures): """Report names of tests with failures (if any).""" if failures: print() print("Tests with failures:") for test, exc_info in failures: print(" ", test) def modules_with_import_problems(self, import_errors): """Report names of modules with import problems (if any).""" if import_errors: print() print("Test-modules with import problems:") for test in import_errors: print(" " + test.module) def format_seconds(self, n_seconds): """Format a time in seconds.""" if n_seconds >= 60: n_minutes, n_seconds = divmod(n_seconds, 60) return "%d minutes %.3f seconds" % (n_minutes, n_seconds) else: return "%.3f seconds" % n_seconds def format_seconds_short(self, n_seconds): """Format a time in seconds (short version).""" return "%.3f s" % n_seconds def summary(self, n_tests, n_failures, n_errors, n_seconds, n_skipped=0): """Summarize the results of a single test layer.""" print(" Ran %s tests with %s failures, %s errors and " "%s skipped in %s." % (n_tests, n_failures, n_errors, n_skipped, self.format_seconds(n_seconds))) def totals(self, n_tests, n_failures, n_errors, n_seconds, n_skipped=0): """Summarize the results of all layers.""" print("Total: %s tests, %s failures, %s errors and %s skipped in %s." % (n_tests, n_failures, n_errors, n_skipped, self.format_seconds(n_seconds))) def list_of_tests(self, tests, layer_name): """Report a list of test names.""" print("Listing %s tests:" % layer_name) for test in tests: print(' ', test) def garbage(self, garbage): """Report garbage generated by tests.""" if garbage: print("Tests generated new (%d) garbage:" % len(garbage)) print(garbage) def test_garbage(self, test, garbage): """Report garbage generated by a test.""" if garbage: print("The following test left garbage:") print(test) print(garbage) def test_threads(self, test, new_threads): """Report threads left behind by a test.""" if new_threads: print("The following test left new threads behind:") print(test) print("New thread(s):", new_threads) def test_cycles(self, test, cycles): """Report cyclic garbage left behind by a test.""" if cycles: print("The following test left cyclic garbage behind:") print(test) for i, cy in enumerate(cycles): print("Cycle", i + 1) for oi in cy: print(" * ", "\n ".join(oi)) def refcounts(self, rc, prev): """Report a change in reference counts.""" print(" sys refcount=%-8d change=%-6d" % (rc, rc - prev)) def detailed_refcounts(self, track, rc, prev): """Report a change in reference counts, with extra detail.""" print(" sum detail refcount=%-8d" " sys refcount=%-8d" " change=%-6d" % (track.n, rc, rc - prev)) track.output() def start_set_up(self, layer_name): """Report that we're setting up a layer. The next output operation should be stop_set_up(). """ print(" Set up %s" % layer_name, end=' ') sys.stdout.flush() def stop_set_up(self, seconds): """Report that we've set up a layer. Should be called right after start_set_up(). """ print("in %s." % self.format_seconds(seconds)) def start_tear_down(self, layer_name): """Report that we're tearing down a layer. The next output operation should be stop_tear_down() or tear_down_not_supported(). """ print(" Tear down %s" % layer_name, end=' ') sys.stdout.flush() def stop_tear_down(self, seconds): """Report that we've tore down a layer. Should be called right after start_tear_down(). """ print("in %s." % self.format_seconds(seconds)) def tear_down_not_supported(self): """Report that we could not tear down a layer. Should be called right after start_tear_down(). """ print("... not supported") def start_test(self, test, tests_run, total_tests): """Report that we're about to run a test. The next output operation should be test_success(), test_error(), or test_failure(). """ self.test_width = 0 if self.progress: if self.last_width: sys.stdout.write('\r' + (' ' * self.last_width) + '\r') s = " %d/%d (%.1f%%)" % (tests_run, total_tests, tests_run * 100.0 / total_tests) sys.stdout.write(s) self.test_width += len(s) if self.verbose == 1: room = self.max_width - self.test_width - 1 s = self.getShortDescription(test, room) sys.stdout.write(s) self.test_width += len(s) elif self.verbose == 1: sys.stdout.write('.' * test.countTestCases()) elif self.in_subprocess: sys.stdout.write('.' * test.countTestCases()) # Give the parent process a new line so it sees the progress # in a timely manner. sys.stdout.write('\n') if self.verbose > 1: s = str(test) sys.stdout.write(' ') sys.stdout.write(s) self.test_width += len(s) + 1 sys.stdout.flush() def test_success(self, test, seconds): """Report that a test was successful. Should be called right after start_test(). The next output operation should be stop_test(). """ if self.verbose > 2: s = " (%s)" % self.format_seconds_short(seconds) sys.stdout.write(s) self.test_width += len(s) + 1 def test_skipped(self, test, reason): """Report that a test was skipped. Should be called right after start_test(). The next output operation should be stop_test(). """ if self.verbose > 2: s = " (skipped: %s)" % reason elif self.verbose > 1: s = " (skipped)" else: return sys.stdout.write(s) self.test_width += len(s) + 1 def test_error(self, test, seconds, exc_info, stdout=None, stderr=None): """Report that an error occurred while running a test. Should be called right after start_test(). The next output operation should be stop_test(). """ if self.verbose > 2: print(" (%s)" % self.format_seconds_short(seconds)) print() self.print_traceback("Error in test %s" % test, exc_info) self.print_std_streams(stdout, stderr) self.test_width = self.last_width = 0 def test_failure(self, test, seconds, exc_info, stdout=None, stderr=None): """Report that a test failed. Should be called right after start_test(). The next output operation should be stop_test(). """ if self.verbose > 2: print(" (%s)" % self.format_seconds_short(seconds)) print() self.print_traceback("Failure in test %s" % test, exc_info) self.print_std_streams(stdout, stderr) self.test_width = self.last_width = 0 def print_traceback(self, msg, exc_info): """Report an error with a traceback.""" print() print(msg) print(self.format_traceback(exc_info)) def print_std_streams(self, stdout, stderr): """Emit contents of buffered standard streams.""" if stdout: sys.stdout.write("Stdout:\n") sys.stdout.write(stdout) if not stdout.endswith("\n"): sys.stdout.write("\n") sys.stdout.write("\n") if stderr: sys.stderr.write("Stderr:\n") sys.stderr.write(stderr) if not stderr.endswith("\n"): sys.stderr.write("\n") sys.stderr.write("\n") def format_traceback(self, exc_info): """Format the traceback.""" v = exc_info[1] if isinstance(v, DocTestFailureException): tb = v.args[0] elif isinstance(v, doctest.DocTestFailure): tb = doctest_template % ( v.test.filename, v.test.lineno + v.example.lineno + 1, v.test.name, v.example.source, v.example.want, v.got, ) else: tb = "".join(traceback.format_exception(*exc_info)) return tb def stop_test(self, test, gccount): """Clean up the output state after a test.""" if gccount and self.verbose: s = "!" if self.verbose == 1 else " [%d]" % gccount self.test_width += len(s) print(s, end="") if self.progress: self.last_width = self.test_width elif self.verbose > 1: print() sys.stdout.flush() def stop_tests(self): """Clean up the output state after a collection of tests.""" if self.progress and self.last_width: sys.stdout.write('\r' + (' ' * self.last_width) + '\r') if self.verbose == 1 or self.progress: print() def tigetnum(attr, default=None): """Return a value from the terminfo database. Terminfo is used on Unix-like systems to report various terminal attributes (such as width, height or the number of supported colors). Returns ``default`` when the ``curses`` module is not available, or when sys.stdout is not a terminal. """ try: import curses except ImportError: # avoid reimporting a broken module sys.modules['curses'] = None else: # If sys.stdout is not a real file object (e.g. in unit tests that # use various wrappers), you get an error, different depending on # Python version: expected_exceptions = ( curses.error, TypeError, AttributeError, io.UnsupportedOperation) try: curses.setupterm() except expected_exceptions: # You get curses.error when $TERM is set to an unknown name pass else: try: return curses.tigetnum(attr) except expected_exceptions: # You get TypeError on PyPy3 due to a bug: # https://bitbucket.org/pypy/pypy/issue/2016/pypy3-cursestigetnum-raises-ctype pass return default def terminal_has_colors(): """Determine whether the terminal supports colors. Some terminals (e.g. the emacs built-in one) don't. """ return tigetnum('colors', -1) >= 8 class ColorfulOutputFormatter(OutputFormatter): """Output formatter that uses ANSI color codes. Like syntax highlighting in your text editor, colorizing test failures helps the developer. """ # These colors are carefully chosen to have enough contrast # on terminals with both black and white background. colorscheme = {'normal': 'normal', 'default': 'default', 'info': 'normal', 'suboptimal-behaviour': 'magenta', 'error': 'brightred', 'number': 'green', 'slow-test': 'brightmagenta', 'ok-number': 'green', 'error-number': 'brightred', 'filename': 'lightblue', 'lineno': 'lightred', 'testname': 'lightcyan', 'failed-example': 'cyan', 'expected-output': 'green', 'actual-output': 'red', 'character-diffs': 'magenta', 'diff-chunk': 'magenta', 'exception': 'red', 'skipped': 'brightyellow', } # Map prefix character to color in diff output. This handles ndiff and # udiff correctly, but not cdiff. In cdiff we ought to highlight '!' as # expected-output until we see a '-', then highlight '!' as actual-output, # until we see a '*', then switch back to highlighting '!' as # expected-output. Nevertheless, coloried cdiffs are reasonably readable, # so I'm not going to fix this. # -- mgedmin diff_color = {'-': 'expected-output', '+': 'actual-output', '?': 'character-diffs', '@': 'diff-chunk', '*': 'diff-chunk', '!': 'actual-output', } prefixes = [('dark', '0;'), ('light', '1;'), ('bright', '1;'), ('bold', '1;'), ] colorcodes = {'default': 0, 'normal': 0, 'black': 30, 'red': 31, 'green': 32, 'brown': 33, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'grey': 37, 'gray': 37, 'white': 37} slow_test_threshold = 10.0 # seconds def color_code(self, color): """Convert a color description (e.g. 'lightred') to a terminal code.""" prefix_code = '' for prefix, code in self.prefixes: if color.startswith(prefix): color = color[len(prefix):] prefix_code = code break color_code = self.colorcodes[color] return f'\033[{prefix_code}{color_code}m' def color(self, what): """Pick a named color from the color scheme""" return self.color_code(self.colorscheme[what]) def colorize(self, what, message, normal='normal'): """Wrap message in color.""" return self.color(what) + message + self.color(normal) def error_count_color(self, n): """Choose a color for the number of errors.""" if n: return self.color('error-number') else: return self.color('ok-number') def skip_count_color(self, n): """Choose a color for the number of skipped tests.""" if n: return self.color('skipped') else: return self.color('ok-number') def test_skipped(self, test, reason): """Report that a test was skipped. Should be called right after start_test(). The next output operation should be stop_test(). """ if self.verbose > 2: s = " ({}skipped: {}{})".format( self.color('skipped'), reason, self.color('info')) elif self.verbose > 1: s = " ({}skipped{})".format( self.color('skipped'), self.color('info')) else: return sys.stdout.write(s) self.test_width += len(s) + 1 def info(self, message): """Print an informative message.""" print(self.colorize('info', message)) def info_suboptimal(self, message): """Print an informative message about losing some of the features. For example, when you run some tests in a subprocess, you lose the ability to use the debugger. """ print(self.colorize('suboptimal-behaviour', message)) def error(self, message): """Report an error.""" print(self.colorize('error', message)) def error_with_banner(self, message): """Report an error with a big ASCII banner.""" print() print(self.colorize('error', '*' * 70)) self.error(message) print(self.colorize('error', '*' * 70)) print() def tear_down_not_supported(self): """Report that we could not tear down a layer. Should be called right after start_tear_down(). """ print("...", self.colorize('suboptimal-behaviour', "not supported")) def format_seconds(self, n_seconds, normal='normal'): """Format a time in seconds.""" if n_seconds >= 60: n_minutes, n_seconds = divmod(n_seconds, 60) return "{} minutes {} seconds".format( self.colorize('number', '%d' % n_minutes, normal), self.colorize('number', '%.3f' % n_seconds, normal)) else: return "%s seconds" % ( self.colorize('number', '%.3f' % n_seconds, normal)) def format_seconds_short(self, n_seconds): """Format a time in seconds (short version).""" if n_seconds >= self.slow_test_threshold: color = 'slow-test' else: color = 'number' return self.colorize(color, "%.3f s" % n_seconds) def summary(self, n_tests, n_failures, n_errors, n_seconds, n_skipped=0): """Summarize the results.""" sys.stdout.writelines([ self.color('info'), ' Ran ', self.color('number'), str(n_tests), self.color('info'), ' tests with ', self.error_count_color(n_failures), str(n_failures), self.color('info'), ' failures, ', self.error_count_color(n_errors), str(n_errors), self.color('info'), ' errors, ', self.skip_count_color(n_skipped), str(n_skipped), self.color('info'), ' skipped in ', self.format_seconds(n_seconds, 'info'), '.', self.color('normal'), '\n', ]) def totals(self, n_tests, n_failures, n_errors, n_seconds, n_skipped=0): """Report totals (number of tests, failures, and errors).""" sys.stdout.writelines([ self.color('info'), 'Total: ', self.color('number'), str(n_tests), self.color('info'), ' tests, ', self.error_count_color(n_failures), str(n_failures), self.color('info'), ' failures, ', self.error_count_color(n_errors), str(n_errors), self.color('info'), ' errors, ', self.skip_count_color(n_skipped), str(n_skipped), self.color('info'), ' skipped in ', self.format_seconds(n_seconds, 'info'), '.', self.color('normal'), '\n']) def print_traceback(self, msg, exc_info): """Report an error with a traceback.""" print() print(self.colorize('error', msg)) v = exc_info[1] if isinstance(v, DocTestFailureException): self.print_doctest_failure(v.args[0]) elif isinstance(v, doctest.DocTestFailure): # I don't think these are ever used... -- mgedmin tb = self.format_traceback(exc_info) print(tb) else: tb = self.format_traceback(exc_info) self.print_colorized_traceback(tb) def print_doctest_failure(self, formatted_failure): """Report a doctest failure. ``formatted_failure`` is a string -- that's what DocTestSuite/DocFileSuite gives us. """ color_of_indented_text = 'normal' colorize_diff = False for line in formatted_failure.splitlines(): if line.startswith('File '): m = re.match(r'File "(.*)", line (\d*), in (.*)$', line) if m: filename, lineno, test = m.groups() sys.stdout.writelines([ self.color('normal'), 'File "', self.color('filename'), filename, self.color('normal'), '", line ', self.color('lineno'), lineno, self.color('normal'), ', in ', self.color('testname'), test, self.color('normal'), '\n']) else: print(line) elif line.startswith(' ') or line.strip() == '': if colorize_diff and len(line) > 4: color = self.diff_color.get( line[4], color_of_indented_text) print(self.colorize(color, line)) else: if line.strip() != '': print(self.colorize(color_of_indented_text, line)) else: print(line) else: colorize_diff = False if line.startswith('Failed example'): color_of_indented_text = 'failed-example' elif line.startswith('Expected:'): color_of_indented_text = 'expected-output' elif line.startswith('Got:'): color_of_indented_text = 'actual-output' elif line.startswith('Exception raised:'): color_of_indented_text = 'exception' elif line.startswith('Differences '): color_of_indented_text = 'normal' colorize_diff = True else: color_of_indented_text = 'normal' print(line) print() def print_colorized_traceback(self, formatted_traceback): """Report a test failure. ``formatted_traceback`` is a string. """ for line in formatted_traceback.splitlines(): if line.startswith(' File'): m = re.match(r' File "(.*)", line (\d*), in (.*)$', line) if m: filename, lineno, test = m.groups() sys.stdout.writelines([ self.color('normal'), ' File "', self.color('filename'), filename, self.color('normal'), '", line ', self.color('lineno'), lineno, self.color('normal'), ', in ', self.color('testname'), test, self.color('normal'), '\n']) else: print(line) elif line.startswith(' '): print(self.colorize('failed-example', line)) elif line.startswith('Traceback (most recent call last)'): print(line) else: print(self.colorize('exception', line)) print() class FakeTest: """A fake test object that only has an id.""" failureException = None def __init__(self, test_id): self._id = test_id def id(self): return self._id # Conditional imports: we don't want zope.testrunner to have a hard # dependency on subunit. try: import subunit from iso8601 import UTC subunit.StreamResultToBytes except (ImportError, AttributeError): subunit = None # testtools is a hard dependency of subunit itself, but we guard it # separately for richer error messages. try: import testtools from testtools.content import Content from testtools.content import ContentType from testtools.content import content_from_file from testtools.content import text_content testtools.StreamToExtendedDecorator except (ImportError, AttributeError): testtools = None class _RunnableDecorator: """Permit controlling the runnable annotation on tests. This decorates a StreamResult, adding a setRunnable context manager to indicate whether a test is runnable. (A context manager is unidiomatic here, but it's just about the simplest way to stuff the relevant state through the various layers of decorators involved without accidentally affecting later test results.) """ def __init__(self, decorated): self.decorated = decorated self._runnable = True def __getattr__(self, name): return getattr(self.decorated, name) @contextmanager def setRunnable(self, runnable): orig_runnable = self._runnable try: self._runnable = runnable yield finally: self._runnable = orig_runnable def status(self, **kwargs): kwargs = dict(kwargs) kwargs['runnable'] = self._runnable self.decorated.status(**kwargs) class _SortedDict(MutableMapping): """A dict that always returns items in sorted order. This differs from collections.OrderedDict in that it returns items in *sorted* order, not in insertion order. We use this as a workaround for the fact that testtools.ExtendedToStreamDecorator doesn't sort the details dict when encoding it, which makes it difficult to write stable doctests for subunit v2 output. """ def __init__(self, items): self._dict = dict(items) def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): self._dict[key] = value def __delitem__(self, key): del self._dict[key] def __iter__(self): return iter(sorted(self._dict)) def __len__(self): return len(self._dict) class SubunitOutputFormatter: """A subunit output formatter. This output formatter generates subunit-compatible output (see https://launchpad.net/subunit). Subunit output is essentially a stream of results of unit tests. In this formatter, non-test events (such as layer set up) are encoded as specially-tagged tests. In particular, for a layer 'foo', the fake tests related to layer setup and teardown are tagged with 'zope:layer' and are called 'foo:setUp' and 'foo:tearDown'. Any tests within layer 'foo' are tagged with 'zope:layer:foo'. Note that all tags specific to this formatter begin with 'zope:'. """ # subunit output is designed for computers, so displaying a progress bar # isn't helpful. progress = False verbose = property(lambda self: self.options.verbose) TAG_INFO_SUBOPTIMAL = 'zope:info_suboptimal' TAG_ERROR_WITH_BANNER = 'zope:error_with_banner' TAG_LAYER = 'zope:layer' TAG_IMPORT_ERROR = 'zope:import_error' TAG_PROFILER_STATS = 'zope:profiler_stats' TAG_GARBAGE = 'zope:garbage' TAG_THREADS = 'zope:threads' TAG_REFCOUNTS = 'zope:refcounts' def __init__(self, options, stream=None): if subunit is None: raise Exception('Requires python-subunit 1.4.3 or better') if testtools is None: raise Exception('Requires testtools 0.9.30 or better') self.options = options if stream is None: stream = sys.stdout self._stream = stream self._subunit = self._subunit_factory(self._stream) # Used to track the last layer that was set up or torn down. Either # None or (layer_name, last_touched_time). self._last_layer = None self.UTC = UTC # Content types used in the output. self.TRACEBACK_CONTENT_TYPE = ContentType( 'text', 'x-traceback', {'language': 'python', 'charset': 'utf8'}) self.PROFILE_CONTENT_TYPE = ContentType( 'application', 'x-binary-profile') self.PLAIN_TEXT = ContentType('text', 'plain', {'charset': 'utf8'}) @classmethod def _subunit_factory(cls, stream): """Return a TestResult attached to the given stream.""" return _RunnableDecorator(subunit.TestProtocolClient(stream)) def _emit_timestamp(self, now=None): """Emit a timestamp to the subunit stream. If 'now' is not specified, use the current time on the system clock. """ if now is None: now = datetime.now(self.UTC) self._subunit.time(now) return now def _emit_fake_test(self, message, tag, details=None): """Emit a successful fake test to the subunit stream. Use this to print tagged informative messages. """ test = FakeTest(message) with self._subunit.setRunnable(False): self._subunit.startTest(test) self._subunit.tags([tag], []) self._subunit.addSuccess(test, details=details) self._subunit.stopTest(test) def _emit_error(self, error_id, tag, exc_info, runnable=False): """Emit an error to the subunit stream. Use this to pass on information about errors that occur outside of tests. """ test = FakeTest(error_id) with self._subunit.setRunnable(runnable): self._subunit.startTest(test) self._subunit.tags([tag], []) self._subunit.addError(test, exc_info) self._subunit.stopTest(test) def _emit_failure(self, failure_id, tag, exc_info): """Emit an failure to the subunit stream. Use this to pass on information about failures that occur outside of tests. """ test = FakeTest(failure_id) self._subunit.addFailure(test, exc_info) def _enter_layer(self, layer_name): """Tell subunit that we are entering a layer.""" self._subunit.tags([f'zope:layer:{layer_name}'], []) def _exit_layer(self, layer_name): """Tell subunit that we are exiting a layer.""" self._subunit.tags([], [f'zope:layer:{layer_name}']) def info(self, message): """Print an informative message.""" # info() output is not relevant to actual test results. It only # says things like "Running tests" or "Tearing down left over # layers", things that are communicated already by the subunit # stream. Just suppress the info() output. pass def info_suboptimal(self, message): """Print an informative message about losing some of the features. For example, when you run some tests in a subprocess, you lose the ability to use the debugger. """ # Used _only_ to indicate running in a subprocess. self._emit_fake_test(message.strip(), self.TAG_INFO_SUBOPTIMAL) def error(self, message): """Report an error.""" # XXX: Mostly used for user errors, sometimes used for errors in the # test framework, sometimes used to record layer setUp failure (!!!). self._stream.write(f'{message}\n') def error_with_banner(self, message): """Report an error with a big ASCII banner.""" # Either "Could not communicate with subprocess" # Or "Can't post-mortem debug when running a layer as a subprocess!" self._emit_fake_test(message, self.TAG_ERROR_WITH_BANNER) def profiler_stats(self, stats): """Report profiler stats.""" fd, filename = tempfile.mkstemp(prefix='zope.testrunner-') os.close(fd) try: stats.dump_stats(filename) profile_content = content_from_file( filename, content_type=self.PROFILE_CONTENT_TYPE) details = {'profiler-stats': profile_content} # Name the test 'zope:profiler_stats' just like its tag. self._emit_fake_test( self.TAG_PROFILER_STATS, self.TAG_PROFILER_STATS, details) finally: os.unlink(filename) def import_errors(self, import_errors): """Report test-module import errors (if any).""" if import_errors: for error in import_errors: self._emit_error( error.module, self.TAG_IMPORT_ERROR, error.exc_info, runnable=True) def tests_with_errors(self, errors): """Report names of tests with errors (if any). Simply not supported by the subunit formatter. Fancy summary output doesn't make sense. """ pass def tests_with_failures(self, failures): """Report names of tests with failures (if any). Simply not supported by the subunit formatter. Fancy summary output doesn't make sense. """ pass def modules_with_import_problems(self, import_errors): """Report names of modules with import problems (if any).""" # This is simply a summary method, and subunit output doesn't # benefit from summaries. pass def summary(self, n_tests, n_failures, n_errors, n_seconds, n_skipped=0): """Summarize the results of a single test layer. Since subunit is a stream protocol format, it has no need for a summary. When the stream is finished other tools can generate a summary if so desired. """ pass def totals(self, n_tests, n_failures, n_errors, n_seconds, n_skipped=0): """Summarize the results of all layers. Simply not supported by the subunit formatter. Fancy summary output doesn't make sense. """ pass def _emit_exists(self, test): """Emit an indication that a test exists. With the v1 protocol, we just emit a fake success line. """ self._subunit.addSuccess(test) def list_of_tests(self, tests, layer_name): """Report a list of test names.""" self._enter_layer(layer_name) for test in tests: self._subunit.startTest(test) self._emit_exists(test) self._subunit.stopTest(test) self._exit_layer(layer_name) def garbage(self, garbage): """Report garbage generated by tests.""" # XXX: Really, 'garbage', 'profiler_stats' and the 'refcounts' twins # ought to add extra details to a fake test that represents the # summary information for the whole suite. However, there's no event # on output formatters for "everything is really finished, honest". -- # jml, 2010-02-14 details = {'garbage': text_content(str(garbage))} self._emit_fake_test(self.TAG_GARBAGE, self.TAG_GARBAGE, details) def test_garbage(self, test, garbage): """Report garbage generated by a test. Encoded in the subunit stream as a test error. Clients can filter out these tests based on the tag if they don't think garbage should fail the test run. """ # XXX: Perhaps 'test_garbage' and 'test_threads' ought to be within # the output for the actual test, appended as details to whatever # result the test gets. Not an option with the present API, as there's # no event for "no more output for this test". -- jml, 2010-02-14 self._subunit.startTest(test) self._subunit.tags([self.TAG_GARBAGE], []) self._subunit.addError( test, details={'garbage': text_content(str(garbage))}) self._subunit.stopTest(test) def test_threads(self, test, new_threads): """Report threads left behind by a test. Encoded in the subunit stream as a test error. Clients can filter out these tests based on the tag if they don't think left-over threads should fail the test run. """ self._subunit.startTest(test) self._subunit.tags([self.TAG_THREADS], []) self._subunit.addError( test, details={'threads': text_content(str(new_threads))}) self._subunit.stopTest(test) def test_cycles(self, test, cycles): """Report cycles left behind by a test.""" pass # not implemented def refcounts(self, rc, prev): """Report a change in reference counts.""" details = _SortedDict({ 'sys-refcounts': text_content(str(rc)), 'changes': text_content(str(rc - prev)), }) # XXX: Emit the details dict as JSON? self._emit_fake_test(self.TAG_REFCOUNTS, self.TAG_REFCOUNTS, details) def detailed_refcounts(self, track, rc, prev): """Report a change in reference counts, with extra detail.""" details = _SortedDict({ 'sys-refcounts': text_content(str(rc)), 'changes': text_content(str(rc - prev)), 'track': text_content(str(track.delta)), }) self._emit_fake_test(self.TAG_REFCOUNTS, self.TAG_REFCOUNTS, details) def start_set_up(self, layer_name): """Report that we're setting up a layer. We do this by emitting a fake test of the form '$LAYER_NAME:setUp' and adding a tag of the form 'zope:layer:$LAYER_NAME' to the current tag context. The next output operation should be stop_set_up(). """ test = FakeTest(f'{layer_name}:setUp') now = self._emit_timestamp() with self._subunit.setRunnable(False): self._subunit.startTest(test) self._subunit.tags([self.TAG_LAYER], []) self._last_layer = (layer_name, now) def stop_set_up(self, seconds): """Report that we've set up a layer. Should be called right after start_set_up(). """ layer_name, start_time = self._last_layer self._last_layer = None test = FakeTest(f'{layer_name}:setUp') self._emit_timestamp(start_time + timedelta(seconds=seconds)) with self._subunit.setRunnable(False): self._subunit.addSuccess(test) self._subunit.stopTest(test) self._enter_layer(layer_name) def layer_failure(self, failure_type, exc_info): layer_name, start_time = self._last_layer self._emit_failure( f'{layer_name}:{failure_type}', self.TAG_LAYER, exc_info) def start_tear_down(self, layer_name): """Report that we're tearing down a layer. We do this by emitting a fake test of the form '$LAYER_NAME:tearDown' and removing a tag of the form 'layer:$LAYER_NAME' from the current tag context. The next output operation should be stop_tear_down() or tear_down_not_supported(). """ test = FakeTest(f'{layer_name}:tearDown') self._exit_layer(layer_name) now = self._emit_timestamp() with self._subunit.setRunnable(False): self._subunit.startTest(test) self._subunit.tags([self.TAG_LAYER], []) self._last_layer = (layer_name, now) def stop_tear_down(self, seconds): """Report that we've torn down a layer. Should be called right after start_tear_down(). """ layer_name, start_time = self._last_layer self._last_layer = None test = FakeTest(f'{layer_name}:tearDown') self._emit_timestamp(start_time + timedelta(seconds=seconds)) with self._subunit.setRunnable(False): self._subunit.addSuccess(test) self._subunit.stopTest(test) def tear_down_not_supported(self): """Report that we could not tear down a layer. Should be called right after start_tear_down(). """ layer_name, start_time = self._last_layer self._last_layer = None test = FakeTest(f'{layer_name}:tearDown') self._emit_timestamp() with self._subunit.setRunnable(False): self._subunit.addSkip(test, 'tearDown not supported') self._subunit.stopTest(test) def start_test(self, test, tests_run, total_tests): """Report that we're about to run a test. The next output operation should be test_success(), test_error(), or test_failure(). """ self._emit_timestamp() self._subunit.startTest(test) def test_success(self, test, seconds): """Report that a test was successful. Should be called right after start_test(). The next output operation should be stop_test(). """ self._emit_timestamp() self._subunit.addSuccess(test) def test_skipped(self, test, reason): """Report that a test was skipped. Should be called right after start_test(). The next output operation should be stop_test(). """ self._subunit.addSkip(test, reason) def _exc_info_to_details(self, exc_info): """Translate 'exc_info' into a details dict usable with subunit.""" # In an ideal world, we'd use the pre-bundled 'TracebackContent' # class from testtools. However, 'OutputFormatter' contains special # logic to handle errors from doctests, so we have to use that and # manually create an object equivalent to an instance of # 'TracebackContent'. formatter = OutputFormatter(None) traceback = formatter.format_traceback(exc_info) # We have no idea if the traceback is a str object or a # bytestring with non-ASCII characters. We had best be careful when # handling it. if isinstance(traceback, bytes): # Assume the traceback was UTF-8-encoded, but still be careful. str_tb = traceback.decode('utf-8', 'replace') else: str_tb = traceback return _SortedDict({ 'traceback': Content( self.TRACEBACK_CONTENT_TYPE, lambda: [str_tb.encode('utf8')]), }) def _add_std_streams_to_details(self, details, stdout, stderr): """Add buffered standard stream contents to a subunit details dict.""" if stdout: if isinstance(stdout, bytes): stdout = stdout.decode('utf-8', 'replace') details['test-stdout'] = Content( self.PLAIN_TEXT, lambda: [stdout.encode('utf-8')]) if stderr: if isinstance(stderr, bytes): stderr = stderr.decode('utf-8', 'replace') details['test-stderr'] = Content( self.PLAIN_TEXT, lambda: [stderr.encode('utf-8')]) def test_error(self, test, seconds, exc_info, stdout=None, stderr=None): """Report that an error occurred while running a test. Should be called right after start_test(). The next output operation should be stop_test(). """ self._emit_timestamp() details = self._exc_info_to_details(exc_info) self._add_std_streams_to_details(details, stdout, stderr) self._subunit.addError(test, details=details) def test_failure(self, test, seconds, exc_info, stdout=None, stderr=None): """Report that a test failed. Should be called right after start_test(). The next output operation should be stop_test(). """ self._emit_timestamp() details = self._exc_info_to_details(exc_info) self._add_std_streams_to_details(details, stdout, stderr) self._subunit.addFailure(test, details=details) def stop_test(self, test, gccount): """Clean up the output state after a test.""" self._subunit.stopTest(test) def stop_tests(self): """Clean up the output state after a collection of tests.""" # subunit handles all of this itself. pass class SubunitV2OutputFormatter(SubunitOutputFormatter): """A subunit v2 output formatter.""" @classmethod def _subunit_factory(cls, stream): """Return a TestResult attached to the given stream.""" stream_result = _RunnableDecorator(subunit.StreamResultToBytes(stream)) result = testtools.ExtendedToStreamDecorator(stream_result) # Lift our decorating method up so that we can get at it easily. result.setRunnable = stream_result.setRunnable result.startTestRun() return result def error(self, message): """Report an error.""" # XXX: Mostly used for user errors, sometimes used for errors in the # test framework, sometimes used to record layer setUp failure (!!!). self._subunit.status( file_name='error', file_bytes=str(message).encode('utf-8'), eof=True, mime_type=repr(self.PLAIN_TEXT)) def _emit_exists(self, test): """Emit an indication that a test exists.""" now = datetime.now(self.UTC) self._subunit.status( test_id=test.id(), test_status='exists', test_tags=self._subunit.current_tags, timestamp=now) @dataclass class TestSuiteInfo: testCases: list = field(default_factory=list) errors: int = 0 failures: int = 0 time: float = 0.0 @property def tests(self): return len(self.testCases) @property def successes(self): return self.tests - self.errors - self.failures @dataclass class TestCaseInfo: test: object time: float testClassName: str testName: str failure: bool = None error: bool = None def get_test_class_name(test): """Compute the test class name from the test object.""" return f'{test.__module__}.{test.__class__.__name__}' def filename_to_suite_name_parts(filename): # lop off whatever portion of the path we have in common # with the current working directory; crude, but about as # much as we can do :( filenameParts = Path(filename).parts cwdParts = Path.cwd().parts longest = min(len(filenameParts), len(cwdParts)) for i in range(longest): if filenameParts[i] != cwdParts[i]: break if i < len(filenameParts) - 1: # The real package name couldn't have a '.' in it. This # makes sense for the common egg naming patterns, and # will still work in other cases suiteNameParts = [] for part in reversed(filenameParts[i:-1]): if '.' in part: break suiteNameParts.insert(0, part) # don't lose the filename, which would have a . in it suiteNameParts.append(filenameParts[-1]) return suiteNameParts def parse_doc_file_case(test): if not isinstance(test, doctest.DocFileCase): return None, None, None filename = test._dt_test.filename suiteNameParts = filename_to_suite_name_parts(filename) testSuite = f'doctest-{"-".join(suiteNameParts)}' testName = test._dt_test.name testClassName = '.'.join(suiteNameParts[:-1]) return testSuite, testName, testClassName def parse_doc_test_case(test): if not isinstance(test, doctest.DocTestCase): return None, None, None testDottedNameParts = test._dt_test.name.split('.') testClassName = get_test_class_name(test) testSuite = testClassName = '.'.join(testDottedNameParts[:-1]) testName = testDottedNameParts[-1] return testSuite, testName, testClassName def parse_manuel(test): if not (HAVE_MANUEL and isinstance(test, manuel.testing.TestCase)): return None, None, None filename = test.regions.location suiteNameParts = filename_to_suite_name_parts(filename) testSuite = f'manuel-{"-".join(suiteNameParts)}' testName = suiteNameParts[-1] testClassName = '.'.join(suiteNameParts[:-1]) return testSuite, testName, testClassName def parse_startup_failure(test): if not isinstance(test, StartUpFailure): return None, None, None testModuleName = test.module return testModuleName, 'Startup', testModuleName def parse_unittest(test): testId = test.id() if testId is None: return None, None, None testClassName = get_test_class_name(test) testSuite = testClassName testName = testId[len(testClassName) + 1:] return testSuite, testName, testClassName class XMLOutputFormattingWrapper: """Output formatter which delegates to another formatter for all operations, but also prepares an element tree of test output. """ def __init__(self, delegate, folder): self.delegate = delegate self._testSuites = {} # test class -> list of test names self.folder = folder def __getattr__(self, name): return getattr(self.delegate, name) def test_failure(self, test, seconds, exc_info, stdout=None, stderr=None): self._record(test, seconds, failure=exc_info) # stdout and stderr are only passed into us when using buffering # (--buffer). self.delegate also comes from zope.testrunner then. if stdout is None and stderr is None: # the normal case return self.delegate.test_failure(test, seconds, exc_info) return self.delegate.test_failure( test, seconds, exc_info, stdout=stdout, stderr=stderr ) def test_error(self, test, seconds, exc_info, stdout=None, stderr=None): self._record(test, seconds, error=exc_info) if stdout is None and stderr is None: # the normal case return self.delegate.test_error(test, seconds, exc_info) return self.delegate.test_error( test, seconds, exc_info, stdout=stdout, stderr=stderr ) def test_success(self, test, seconds): self._record(test, seconds) return self.delegate.test_success(test, seconds) def import_errors(self, import_errors): if import_errors: for test in import_errors: self._record(test, 0, error=test.exc_info) return self.delegate.import_errors(import_errors) def _record(self, test, seconds, failure=None, error=None): for parser in [parse_doc_file_case, parse_doc_test_case, parse_manuel, parse_startup_failure, parse_unittest]: testSuite, testName, testClassName = parser(test) if (testSuite, testName, testClassName) != (None, None, None): break if (testSuite, testName, testClassName) == (None, None, None): raise TypeError( 'Unknown test type: Could not compute testSuite, testName,' f' testClassName: {test!r}' ) suite = self._testSuites.setdefault(testSuite, TestSuiteInfo()) suite.testCases.append(TestCaseInfo( test, seconds, testClassName, testName, failure, error)) if failure is not None: suite.failures += 1 if error is not None: suite.errors += 1 if seconds: suite.time += seconds def writeXMLReports(self, properties={}): timestamp = datetime.now().isoformat() hostname = socket.gethostname() reportsDir = self.folder / 'testreports' reportsDir.mkdir(exist_ok=True) for name, suite in self._testSuites.items(): filename = reportsDir / f'{name}.xml' testSuiteNode = ElementTree.Element('testsuite') testSuiteNode.set('tests', str(suite.tests)) testSuiteNode.set('errors', str(suite.errors)) testSuiteNode.set('failures', str(suite.failures)) testSuiteNode.set('hostname', hostname) testSuiteNode.set('name', name) testSuiteNode.set('time', str(suite.time)) testSuiteNode.set('timestamp', timestamp) propertiesNode = ElementTree.Element('properties') testSuiteNode.append(propertiesNode) for k, v in properties.items(): propertyNode = ElementTree.Element('property') propertiesNode.append(propertyNode) propertyNode.set('name', k) propertyNode.set('value', v) for testCase in suite.testCases: testCaseNode = ElementTree.Element('testcase') testSuiteNode.append(testCaseNode) testCaseNode.set('classname', testCase.testClassName) testCaseNode.set('name', testCase.testName) testCaseNode.set('time', str(testCase.time)) if testCase.error: errorNode = ElementTree.Element('error') testCaseNode.append(errorNode) try: excType, excInstance, tb = testCase.error errorMessage = str(excInstance) stackTrace = ''.join(traceback.format_tb(tb)) finally: # Avoids a memory leak del tb errorNode.set('message', errorMessage.split('\n')[0]) errorNode.set('type', str(excType)) text = (errorMessage + '\n\n' + stackTrace) errorNode.text = text if testCase.failure: failureNode = ElementTree.Element('failure') testCaseNode.append(failureNode) try: excType, excInstance, tb = testCase.failure errorMessage = str(excInstance) stackTrace = ''.join(traceback.format_tb(tb)) except UnicodeEncodeError: errorMessage = 'Could not extract error str ' \ 'for unicode error' stackTrace = ''.join(traceback.format_tb(tb)) finally: # Avoids a memory leak del tb failureNode.set('message', errorMessage.split('\n')[0]) failureNode.set('type', str(excType)) text = f'{errorMessage}\n\n{stackTrace}' failureNode.text = text # We don't have a good way to capture these yet, so they are empty: systemOutNode = ElementTree.Element('system-out') testSuiteNode.append(systemOutNode) systemErrNode = ElementTree.Element('system-err') testSuiteNode.append(systemErrNode) # indent the XML structure with suppress(AttributeError): ElementTree.indent(testSuiteNode) text = ElementTree.tostring(testSuiteNode).decode('utf-8') # Write file with open(filename, 'w') as outputFile: outputFile.write(text) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/garbagecollection.py0000644000076600000240000000426715011314374024337 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Garbage collection support. """ import gc import sys import zope.testrunner.feature class Threshold(zope.testrunner.feature.Feature): def __init__(self, runner): super().__init__(runner) self.threshold = self.runner.options.gc self.active = bool(self.threshold) if not self.active: return if len(self.threshold) > 3: self.runner.options.output.error("Too many --gc options") sys.exit(1) def global_setup(self): self.old_threshold = gc.get_threshold() if self.threshold[0]: self.runner.options.output.info( "Cyclic garbage collection threshold set to: %s" % repr(tuple(self.threshold))) else: self.runner.options.output.info( "Cyclic garbage collection is disabled.") gc.set_threshold(*self.threshold) def global_teardown(self): gc.set_threshold(*self.old_threshold) class Debug(zope.testrunner.feature.Feature): """Manages garbage collection debug flags.""" def __init__(self, runner): super().__init__(runner) self.flags = self.runner.options.gc_option self.active = bool(self.flags) if not self.active: return def global_setup(self): # Set garbage collection debug flags self.old_flags = gc.get_debug() new_flags = 0 for op in self.flags: new_flags |= getattr(gc, op) gc.set_debug(new_flags) def global_teardown(self): gc.set_debug(self.old_flags) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/interfaces.py0000644000076600000240000001064515011314374023013 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test runner interfaces XXX Note: These interfaces are still being sketched out. Please do not rely on them, yet. """ import zope.interface class EndRun(Exception): """Indicate that the existing run call should stop Used to prevent additional test output after post-mortem debugging. """ class IFeature(zope.interface.Interface): """Features extend the test runners functionality in a pipe-lined order. """ active = zope.interface.Attribute( "Flag whether this feature is activated. If it is not activated than " "its methods won't be called by the runner.") def global_setup(): """Executed once when the test runner is being set up.""" def late_setup(): """Executed once right before the actual tests get executed and after all global setups have happened. Should do as little work as possible to avoid timing interferences with other features. It is guaranteed that the calling stack frame is not left until early_teardown was called. """ def layer_setup(layer): """Executed once after a layer was set up.""" def layer_teardown(layer): """Executed once after a layer was run.""" def test_setup(test): """Executed once before each test.""" def test_teardown(test): """Executed once after each test.""" def early_teardown(): """Executed once directly after all tests. This method should do as little as possible to avoid timing issues. It is guaranteed to be called directly from the same stack frame that called `late_setup`. """ def global_teardown(): """Executed once after all tests where run and early teardowns have happened. """ def report(): """Executed once after all tests have been run and all setup was torn down. This is the only method that should produce output. """ class ITestRunner(zope.interface.Interface): """The test runner manages test layers and their execution. The functionality of a test runner can be extended by configuring features. """ options = zope.interface.Attribute( "Provides access to configuration options.") class IMinimalTestLayer(zope.interface.Interface): """A test layer. This is the bare minimum that a ``layer`` attribute on a test suite should provide. """ __bases__ = zope.interface.Attribute( "A tuple of base layers.") __name__ = zope.interface.Attribute( "Name of the layer") __module__ = zope.interface.Attribute( "Dotted name of the module that defines this layer") def __hash__(): """A test layer must be hashable. The default identity-based __eq__ and __hash__ that Python provides usually suffice. """ def __eq__(): """A test layer must be hashable. The default identity-based __eq__ and __hash__ that Python provides usually suffice. """ class IFullTestLayer(IMinimalTestLayer): """A test layer. This is the full list of optional methods that a test layer can specify. """ def setUp(): """Shared layer setup. Called once before any of the tests in this layer (or sublayers) are run. """ def tearDown(): """Shared layer teardown. Called once after all the tests in this layer (and sublayers) are run. May raise NotImplementedError. """ def testSetUp(): """Additional test setup. Called once before every of test in this layer (or sublayers) is run. """ def testTearDown(): """Additional test teardown. Called once after every of test in this layer (or sublayers) is run. """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/layer.py0000644000076600000240000000171415011314374022001 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Layer definitions """ import unittest class UnitTests: """A layer for gathering all unit tests.""" class EmptyLayer: """An empty layer to start spreading out subprocesses.""" __bases__ = () __module__ = '' def EmptySuite(): suite = unittest.TestSuite() suite.layer = EmptyLayer() return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/listing.py0000644000076600000240000000230115011314374022327 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Filter which tests to run. """ import zope.testrunner.feature class Listing(zope.testrunner.feature.Feature): """Lists all tests in the report instead of running the tests.""" def __init__(self, runner): super().__init__(runner) self.active = bool(runner.options.list_tests) def global_setup(self): self.runner.do_run_tests = False self.runner.failed = False def report(self): for layer_name, layer, tests in self.runner.ordered_layers(): self.runner.options.output.list_of_tests(tests, layer_name) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/logsupport.py0000644000076600000240000000460115011314374023101 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Logging support. This code is pretty much untested and was only mechanically refactored. The module name is not 'logging' because of a name collision with Python's logging module. """ import logging import logging.config import os import os.path import warnings import zope.testrunner.feature class Logging(zope.testrunner.feature.Feature): active = True def global_setup(self): # Get the log.ini file either from the envvar # ``ZOPE_TESTRUNNER_LOG_INI`` or file ``log.ini`` in # the current working directory. logini = os.environ.get("ZOPE_TESTRUNNER_LOG_INI") if logini is not None and not os.path.exists(logini): warnings.warn( "ERROR: file specified by envvar ZOPE_TESTRUNNER_LOG_INI` " "does not exist") logini = None if logini is None: logini = "log.ini" logini = os.path.abspath(logini) # make absolute if os.path.exists(logini): logging.config.fileConfig(logini, disable_existing_loggers=False) # remember the log configuration in envvar for use # by child processes os.environ["ZOPE_TESTRUNNER_LOG_INI"] = logini else: # If there's no log.ini, cause the logging package to be # silent during testing. root = logging.getLogger() root.addHandler(NullHandler()) logging.basicConfig() if "LOGGING" in os.environ: level = int(os.environ["LOGGING"]) logging.getLogger().setLevel(level) class NullHandler(logging.Handler): """Logging handler that drops everything on the floor. We require silence in the test environment. Hush. """ def emit(self, record): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/options.py0000644000076600000240000006410715011314374022365 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Command-line option parsing """ import argparse import os import re import sys from importlib.metadata import distribution from zope.testrunner.formatter import ColorfulOutputFormatter from zope.testrunner.formatter import OutputFormatter from zope.testrunner.formatter import SubunitOutputFormatter from zope.testrunner.formatter import SubunitV2OutputFormatter from zope.testrunner.formatter import terminal_has_colors from zope.testrunner.profiling import available_profilers from .util import uses_refcounts def _regex_search(s): return re.compile(s).search parser = argparse.ArgumentParser( description="Discover and run unittest tests") parser.add_argument("legacy_module_filter", nargs="?", help="DEPRECATED: Prefer to use --module.") parser.add_argument("legacy_test_filter", nargs="?", help="DEPRECATED: Prefer to use --test.") ###################################################################### # Searching and filtering searching = parser.add_argument_group("Searching and filtering", """\ Options in this group are used to define which tests to run. """) searching.add_argument( '--package', '--dir', '-s', action="append", dest='package', help="""\ Search the given package's directories for tests. This can be specified more than once to run tests in multiple parts of the source tree. For example, if refactoring interfaces, you don't want to see the way you have broken setups for tests in other packages. You *just* want to run the interface tests. Packages are supplied as dotted names. For compatibility with the old test runner, forward and backward slashed in package names are converted to dots. (In the special case of packages spread over multiple directories, only directories within the test search path are searched. See the --path option.) """) searching.add_argument( '--module', '-m', action="append", dest='module', help="""\ Specify a test-module filter as a regular expression. This is a case-sensitive regular expression, used in search (not match) mode, to limit which test modules are searched for tests. The regular expressions are checked against dotted module names. In an extension of Python regexp notation, a leading "!" is stripped and causes the sense of the remaining regexp to be negated (so "!bc" matches any string that does not match "bc", and vice versa). The option can be specified multiple test-module filters. Test modules matching any of the test filters are searched. If no test-module filter is specified, then all test modules are used. """) searching.add_argument( '--test', '-t', action="append", dest='test', help="""\ Specify a test filter as a regular expression. This is a case-sensitive regular expression, used in search (not match) mode, to limit which tests are run. In an extension of Python regexp notation, a leading "!" is stripped and causes the sense of the remaining regexp to be negated (so "!bc" matches any string that does not match "bc", and vice versa). The option can be specified multiple test filters. Tests matching any of the test filters are included. If no test filter is specified, then all tests are run. """) searching.add_argument( '--unit', '-u', action="store_true", dest='unit', help="""\ Run only unit tests, ignoring any layer options. """) searching.add_argument( '--non-unit', '-f', action="store_true", dest='non_unit', help="""\ Run tests other than unit tests. """) searching.add_argument( '--layer', action="append", dest='layer', help="""\ Specify a test layer to run. The option can be given multiple times to specify more than one layer. If not specified, all layers are run. It is common for the running script to provide default values for this option. Layers are specified regular expressions, used in search mode, for dotted names of objects that define a layer. In an extension of Python regexp notation, a leading "!" is stripped and causes the sense of the remaining regexp to be negated (so "!bc" matches any string that does not match "bc", and vice versa). The layer named 'zope.testrunner.layer.UnitTests' is reserved for unit tests, however, take note of the --unit and non-unit options. """) searching.add_argument( '-a', '--at-level', type=int, dest='at_level', default=1, help="""\ Run the tests at the given level. Any test at a level at or below this is run, any test at a level above this is not run. Level <= 0 runs all tests. """) searching.add_argument( '--all', action="store_true", dest='all', help="Run tests at all levels.") searching.add_argument( '--only-level', type=int, dest='only_level', default=None, help="""\ Run only the tests at the given level. Tests on other levels are not run. Default is None, which means the level is not restricted by this option. If this option is used it overrides `--at-level` and `--all` options. """) searching.add_argument( '--list-tests', action="store_true", dest='list_tests', default=False, help="List all tests that matched your filters. Do not run any tests.") searching.add_argument( '--require-unique', action="store_true", dest='require_unique_ids', default=False, help="""\ Require that all test IDs be unique and raise an error if duplicates are encountered. """) ###################################################################### # Reporting reporting = parser.add_argument_group("Reporting", """\ Reporting options control basic aspects of test-runner output """) reporting.add_argument( '--verbose', '-v', action="count", dest='verbose', default=0, help="""\ Make output more verbose. Increment the verbosity level. """) reporting.add_argument( '--quiet', '-q', action="store_true", dest='quiet', help="""\ Make the output minimal, overriding any verbosity options. """) reporting.add_argument( '--progress', '-p', action="store_true", dest='progress', help="""\ Output progress status """) reporting.add_argument( '--no-progress', action="store_false", dest='progress', help="""\ Do not output progress status. This is the default, but can be used to counter a previous use of --progress or -p. """) # The actual processing will be done in the get_options function, but # we want argparse to generate appropriate help info for us, so we add # an option anyway. reporting.add_argument( '--auto-progress', action="store_const", const=None, help="""\ Output progress status, but only when stdout is a terminal. """) reporting.add_argument( '--color', '-c', action="store_true", dest='color', help="""\ Colorize the output. """) reporting.add_argument( '--no-color', '-C', action="store_false", dest='color', help="""\ Do not colorize the output. This is the default, but can be used to counter a previous use of --color or -c. """) # The actual processing will be done in the get_options function, but # we want argparse to generate appropriate help info for us, so we add # an option anyway. reporting.add_argument( '--auto-color', action="store_const", const=None, help="""\ Colorize the output, but only when stdout is a terminal. """) reporting.add_argument( '--subunit', action="store_true", dest='subunit', help="""\ Use subunit v1 output. Will not be colorized. """) reporting.add_argument( '--subunit-v2', action="store_true", dest='subunit_v2', help="""\ Use subunit v2 output. Will not be colorized. """) reporting.add_argument( '--slow-test', type=float, dest='slow_test_threshold', metavar='N', default=10, help="""\ With -c and -vvv, highlight tests that take longer than N seconds (default: %(default)s). """) reporting.add_argument( '-1', '--hide-secondary-failures', action="store_true", dest='report_only_first_failure', help="""\ Report only the first failure in a doctest. (Examples after the failure are still executed, in case they do any cleanup.) """) reporting.add_argument( '--show-secondary-failures', action="store_false", dest='report_only_first_failure', help="""\ Report all failures in a doctest. This is the default, but can be used to counter a default use of -1 or --hide-secondary-failures. """) reporting.add_argument( '--ndiff', action="store_true", dest="ndiff", help="""\ When there is a doctest failure, show it as a diff using the ndiff.py utility. """) reporting.add_argument( '--udiff', action="store_true", dest="udiff", help="""\ When there is a doctest failure, show it as a unified diff. """) reporting.add_argument( '--cdiff', action="store_true", dest="cdiff", help="""\ When there is a doctest failure, show it as a context diff. """) reporting.add_argument( '--ignore-new-thread', metavar='REGEXP', action="append", default=[], dest='ignore_new_threads', help="""\ If a thread with this name is left behind, don't report this at the end. This is a case-sensitive regular expression, used in match mode. This option can be used multiple times. If a thread name matches any of them, it will be ignored. """) reporting.add_argument( '--buffer', action="store_true", dest="buffer", help="""\ Buffer the standard output and standard error streams during each test. Output during a passing test is discarded. Output during failing or erroring tests is echoed. This option is enabled by default if --subunit or --subunit-v2 is in use, to avoid corrupting the subunit stream. """) reporting.add_argument( '--xml', action='store', dest='xmlOutput', help="""\ If given, XML reports will be written to the specified directory. """) ###################################################################### # Analysis analysis = parser.add_argument_group("Analysis", """\ Analysis options provide tools for analysing test output. """) analysis.add_argument( '--stop-on-error', '--stop', '-x', action="store_true", dest='stop_on_error', help="Stop running tests after first test failure or error." ) analysis.add_argument( '--post-mortem', '--pdb', '-D', action="store_true", dest='post_mortem', help="Enable post-mortem debugging of test failures" ) analysis.add_argument( '--gc', '-g', action="append", dest='gc', type=int, help="""\ Set the garbage collector generation threshold. This can be used to stress memory and gc correctness. Some crashes are only reproducible when the threshold is set to 1 (aggressive garbage collection). Do "--gc 0" to disable garbage collection altogether. The --gc option can be used up to 3 times to specify up to 3 of the 3 Python gc_threshold settings. """) analysis.add_argument( '--gc-option', '-G', action="append", dest='gc_option', choices={'DEBUG_STATS', 'DEBUG_COLLECTABLE', 'DEBUG_UNCOLLECTABLE', 'DEBUG_INSTANCES', 'DEBUG_OBJECTS', 'DEBUG_SAVEALL', 'DEBUG_LEAK'}, help="""\ Set a Python gc-module debug flag. This option can be used more than once to set multiple flags. """) if uses_refcounts: analysis.add_argument( '--gc-after-test', action="store_true", dest='gc_after_test', help="""\ After each test, call 'gc.collect' and record the return value *rv*; when *rv* is non-zero, output '!' on verbosity level 1 and '[*rv*]' on higher verbosity levels.\n On verbosity level 4 or higher output detailed cycle information. """) analysis.add_argument( '--repeat', '-N', action="store", type=int, dest='repeat', default=1, help="""\ Repeat the tests the given number of times. This option is used to make sure that tests leave their environment in the state they found it and, with the --report-refcounts option to look for memory leaks. """) analysis.add_argument( '--report-refcounts', '-r', action="store_true", dest='report_refcounts', help="""\ After each run of the tests, output a report summarizing changes in refcounts by object type. This option that requires that Python was built with the --with-pydebug option to configure. """) analysis.add_argument( '--coverage', action="store", dest='coverage', help="""\ Perform code-coverage analysis, saving trace data to the directory with the given name. A code coverage summary is printed to standard out. """) analysis.add_argument( '--profile', action="store", dest='profile', choices=set(available_profilers), help="""\ Run the tests under cProfiler and display the top 50 stats, sorted by cumulative time and number of calls. """) analysis.add_argument( '--profile-directory', action="store", dest='prof_dir', default='.', help="""\ Directory for temporary profiler files. All files named tests_profile.*.prof in this directory will be removed. If you intend to run multiple instances of the test runner in parallel, be sure to tell them to use different directories, so they won't step on each other's toes. """) ###################################################################### # Setup setup = parser.add_argument_group("Setup", """\ Setup options are normally supplied by the testrunner script, although they can be overridden by users. """) setup.add_argument( '--path', action="append", dest='path', type=os.path.abspath, help="""\ Specify a path to be added to Python's search path. This option can be used multiple times to specify multiple search paths. The path is usually specified by the test-runner script itself, rather than by users of the script, although it can be overridden by users. Only tests found in the path will be run. This option also specifies directories to be searched for tests. See the search_directory. """) setup.add_argument( '--test-path', action="append", dest='test_path', type=os.path.abspath, help="""\ Specify a path to be searched for tests, but not added to the Python search path. This option can be used multiple times to specify multiple search paths. The path is usually specified by the test-runner script itself, rather than by users of the script, although it can be overridden by users. Only tests found in the path will be run. """) setup.add_argument( '--package-path', action="append", dest='package_path', nargs=2, metavar="ARG", help="""\ Specify a path to be searched for tests, but not added to the Python search path. Also specify a package for files found in this path. This is used to deal with directories that are stitched into packages that are not otherwise searched for tests. This option takes 2 arguments specifying the path and the package. This option can be used multiple times to specify multiple search paths. The path is usually specified by the test-runner script itself, rather than by users of the script, although it can be overridden by users. Only tests found in the path will be run. """) setup.add_argument( '--tests-pattern', action="store", dest='tests_pattern', default=_regex_search('^tests$'), type=_regex_search, help="""\ The test runner looks for modules containing tests. It uses this pattern to identify these modules. The modules may be either packages or python files. If a test module is a package, it uses the value given by the test-file-pattern to identify python files within the package containing tests. """) setup.add_argument( '--suite-name', action="store", dest='suite_name', default='test_suite', help="""\ Specify the name of the object in each test_module that contains the module's test suite. """) setup.add_argument( '--test-file-pattern', action="store", dest='test_file_pattern', default=_regex_search('^test'), type=_regex_search, help="""\ Specify a pattern for identifying python files within a tests package. See the documentation for the --tests-pattern option. """) setup.add_argument( '--ignore_dir', action="append", dest='ignore_dir', default=['.git', '.svn', 'CVS', '{arch}', '.arch-ids', '_darcs'], help="""\ Specifies the name of a directory to ignore when looking for tests. """) setup.add_argument( '--shuffle', action="store_true", dest='shuffle', help="""\ Shuffles the order in which tests are ran. """) setup.add_argument( '--shuffle-seed', action="store", dest='shuffle_seed', type=int, help="""\ Value used to initialize the tests shuffler. Specify a value to create repeatable random ordered tests. """) ###################################################################### # Other other = parser.add_argument_group("Other", "Other options") other.add_argument( '--version', action="store_true", dest='showversion', help="Print the version of the testrunner, and exit.") other.add_argument( '-j', action="store", type=int, dest='processes', default=1, help="""\ Use up to given number of parallel processes to execute tests. May decrease test run time substantially. Defaults to %(default)s. """) other.add_argument( '--keepbytecode', '-k', action="store_true", dest='keepbytecode', help="""\ Normally, the test runner scans the test paths and the test directories looking for and deleting pyc or pyo files without corresponding py files. This is to prevent spurious test failures due to finding compiled modules where source modules have been deleted. This scan can be time consuming. Using this option disables this scan. If you know you haven't removed any modules since last running the tests, can make the test run go much faster. """) other.add_argument( '--usecompiled', action="store_true", dest='usecompiled', help="""\ Normally, a package must contain an __init__.py file, and only .py files can contain test code. When this option is specified, compiled Python files (.pyc and .pyo) can be used instead: a directory containing __init__.pyc or __init__.pyo is also considered to be a package, and if file XYZ.py contains tests but is absent while XYZ.pyc or XYZ.pyo exists then the compiled files will be used. This is necessary when running tests against a tree where the .py files have been removed after compilation to .pyc/.pyo. Use of this option implies --keepbytecode. """) other.add_argument( '--exit-with-status', action="store_true", dest='exitwithstatus', help="""DEPRECATED: The test runner will always exit with a status.\ """) ###################################################################### # Command-line processing def merge_options(options, defaults): odict = options.__dict__ for name, value in defaults.__dict__.items(): if (value is not None) and (odict[name] is None): odict[name] = value def get_options(args=None, defaults=None): # Because we want to inspect stdout and decide to colorize or not, we # replace the --auto-color option with the appropriate --color or # --no-color option. That way the subprocess doesn't have to decide (which # it would do incorrectly anyway because stdout would be a pipe). def apply_auto_color(args): if args and '--auto-color' in args: if sys.stdout.isatty() and terminal_has_colors(): colorization = '--color' else: colorization = '--no-color' args[:] = [arg.replace('--auto-color', colorization) for arg in args] # The comment of apply_auto_color applies here as well def apply_auto_progress(args): if args and '--auto-progress' in args: if sys.stdout.isatty(): progress = '--progress' else: progress = '--no-progress' args[:] = [arg.replace('--auto-progress', progress) for arg in args] apply_auto_color(args) apply_auto_color(defaults) apply_auto_progress(args) apply_auto_progress(defaults) if defaults: defaults = parser.parse_args(defaults) else: defaults = None if args is None: args = sys.argv options = parser.parse_args(args[1:], defaults) options.original_testrunner_args = args if options.showversion: dist = distribution('zope.testrunner') print('zope.testrunner version %s' % dist.version) sys.exit(0) if options.subunit or options.subunit_v2: try: import subunit subunit except ImportError: print("""\ Subunit is not installed. Please install Subunit to generate subunit output. """) options.fail = True return options options.buffer = True if options.subunit and options.subunit_v2: print("""\ You may only use one of --subunit and --subunit-v2. """) options.fail = True return options if options.subunit: options.output = SubunitOutputFormatter(options) elif options.subunit_v2: options.output = SubunitV2OutputFormatter(options) elif options.color: options.output = ColorfulOutputFormatter(options) options.output.slow_test_threshold = options.slow_test_threshold else: options.output = OutputFormatter(options) options.fail = False if options.legacy_module_filter: module_filter = options.legacy_module_filter if module_filter != '.': if options.module: options.module.append(module_filter) else: options.module = [module_filter] if options.legacy_test_filter: test_filter = options.legacy_test_filter if options.test: options.test.append(test_filter) else: options.test = [test_filter] options.ignore_dir = set(options.ignore_dir) options.test = options.test or ['.'] module_set = bool(options.module) options.module = options.module or ['.'] options.path = options.path or [] options.test_path = options.test_path or [] options.test_path += options.path options.test_path = ([(path, '') for path in options.test_path] + [(os.path.abspath(path), package) for (path, package) in options.package_path or () ]) if options.package: pkgmap = dict(options.test_path) options.package = [normalize_package(p, pkgmap) for p in options.package] options.prefix = [(path + os.path.sep, package) for (path, package) in options.test_path] # Sort prefixes so that longest prefixes come first. # That is because only first match is evaluated which # can be a problem with nested source packages. options.prefix.sort(key=lambda p: len(p[0]), reverse=True) if options.all: options.at_level = sys.maxsize if options.unit and options.non_unit: # The test runner interprets this as "run only those tests that are # both unit and non-unit at the same time". The user, however, wants # to run both unit and non-unit tests. Disable the filtering so that # the user will get what she wants: options.unit = options.non_unit = False if options.unit: # XXX Argh. options.layer = ['zope.testrunner.layer.UnitTests'] options.layer = options.layer and {layer: 1 for layer in options.layer} if options.usecompiled: options.keepbytecode = options.usecompiled if options.quiet: options.verbose = 0 if options.report_refcounts and options.repeat < 2: print("""\ You must use the --repeat (-N) option to specify a repeat count greater than 1 when using the --report_refcounts (-r) option. """) options.fail = True return options if options.report_refcounts and not hasattr(sys, "gettotalrefcount"): print("""\ The Python you are running was not configured with --with-pydebug. This is required to use the --report-refcounts option. """) options.fail = True return options if module_set and options.require_unique_ids: # We warn if --module and --require-unique are specified at the same # time, though we don't exit. print("""\ You specified a module along with --require-unique; --require-unique will not try to enforce test ID uniqueness when working with a specific module. """) return options def normalize_package(package, package_map=None): r"""Normalize package name passed to the --package option. >>> normalize_package('zope.testrunner') 'zope.testrunner' Converts path names into package names for compatibility with the old test runner. >>> normalize_package('zope/testrunner') 'zope.testrunner' >>> normalize_package('zope/testrunner/') 'zope.testrunner' >>> normalize_package('zope\\testrunner') 'zope.testrunner' Can use a map of absolute pathnames to package names >>> a = os.path.abspath >>> normalize_package('src/zope/testrunner/', ... {a('src'): ''}) 'zope.testrunner' >>> normalize_package('src/zope_testrunner/', ... {a('src/zope_testrunner'): 'zope.testrunner'}) 'zope.testrunner' >>> normalize_package('src/zope_something/tests', ... {a('src/zope_something'): 'zope.something', ... a('src'): ''}) 'zope.something.tests' """ package_map = {} if package_map is None else package_map package = package.replace('\\', '/') if package.endswith('/'): package = package[:-1] bits = package.split('/') for n in range(len(bits), 0, -1): pkg = package_map.get(os.path.abspath('/'.join(bits[:n]))) if pkg is not None: bits = bits[n:] if pkg: bits = [pkg] + bits return '.'.join(bits) return package.replace('/', '.') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/process.py0000644000076600000240000000402015011314374022334 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Subprocess support. """ import sys import zope.testrunner.feature class SubProcess(zope.testrunner.feature.Feature): """Lists all tests in the report instead of running the tests.""" def __init__(self, runner): super().__init__(runner) self.active = bool(runner.options.resume_layer) def global_setup(self): self.original_stderr = sys.stderr sys.stderr = sys.stdout if self.runner.options.processes > 1: # If we only have one subprocess, there's absolutely # no reason to squelch. We will let the messages through in a # timely manner, if they have been requested. On the other hand, if # there are multiple processes, we do squelch to 0. self.runner.options.verbose = 0 self.progress = False def report(self): sys.stdout.close() # Communicate with the parent. The protocol is obvious: print(self.runner.ran, len(self.runner.failures), len(self.runner.errors), file=self.original_stderr) for test, exc_info in self.runner.failures: print(' '.join(str(test).strip().split('\n')), file=self.original_stderr) for test, exc_info in self.runner.errors: print(' '.join(str(test).strip().split('\n')), file=self.original_stderr) self.original_stderr.flush() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/profiling.py0000644000076600000240000000625115011314374022657 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Profiler support for the test runner """ import cProfile import glob import os import pstats import tempfile import zope.testrunner.feature available_profilers = {} class CProfiler: """cProfiler""" def __init__(self, filepath): self.filepath = filepath self.profiler = cProfile.Profile() self.enable = self.profiler.enable self.disable = self.profiler.disable def finish(self): self.profiler.dump_stats(self.filepath) def loadStats(self, prof_glob): stats = None for file_name in glob.glob(prof_glob): if stats is None: stats = pstats.Stats(file_name) else: stats.add(file_name) return stats available_profilers['cProfile'] = CProfiler class Profiling(zope.testrunner.feature.Feature): def __init__(self, runner): super().__init__(runner) self.active = bool(self.runner.options.profile) self.profiler = self.runner.options.profile def global_setup(self): self.prof_prefix = 'tests_profile.' self.prof_suffix = '.prof' self.prof_glob = os.path.join( self.runner.options.prof_dir, self.prof_prefix + '*' + self.prof_suffix) # if we are going to be profiling, and this isn't a subprocess, # clean up any stale results files if not self.runner.options.resume_layer: for file_name in glob.glob(self.prof_glob): os.unlink(file_name) # set up the output file self.oshandle, self.file_path = tempfile.mkstemp( self.prof_suffix, self.prof_prefix, self.runner.options.prof_dir) self.profiler = available_profilers[self.runner.options.profile]( self.file_path) # Need to do this rebinding to support the stack-frame annoyance with # hotshot. self.late_setup = self.profiler.enable self.early_teardown = self.profiler.disable def global_teardown(self): self.profiler.finish() # We must explicitly close the handle mkstemp returned, else on # Windows this dies the next time around just above due to an # attempt to unlink a still-open file. os.close(self.oshandle) if not self.runner.options.resume_layer: self.profiler_stats = self.profiler.loadStats(self.prof_glob) self.profiler_stats.sort_stats('cumulative', 'calls') def report(self): if not self.runner.options.resume_layer: self.runner.options.output.profiler_stats(self.profiler_stats) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/refcount.py0000644000076600000240000000614215011314374022512 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Support for tracking reference counts. """ import gc import sys import types class TrackRefs: """Object to track reference counts across test runs.""" def __init__(self): self.type2count = {} self.type2all = {} self.delta = None self.n = 0 self.update() self.delta = None def update(self): gc.collect() obs = sys.getobjects(0) type2count = {} type2all = {} n = 0 for o in obs: if type(o) is str and o == '': # avoid dictionary madness continue all = sys.getrefcount(o) - 3 n += all t = type(o) if issubclass(t, types.InstanceType): t = o.__class__ if t in type2count: type2count[t] += 1 type2all[t] += all else: type2count[t] = 1 type2all[t] = all ct = [( type_or_class_title(t), type2count[t] - self.type2count.get(t, 0), type2all[t] - self.type2all.get(t, 0), ) for t in type2count] ct += [( type_or_class_title(t), - self.type2count[t], - self.type2all[t], ) for t in self.type2count if t not in type2count] ct.sort() self.delta = ct self.type2count = type2count self.type2all = type2all self.n = n def output(self): printed = False s1 = s2 = 0 for t, delta1, delta2 in self.delta: if delta1 or delta2: if not printed: print( ' Leak details, changes in instances and refcounts' ' by type/class:') print( " %-55s %6s %6s" % ('type/class', 'insts', 'refs')) print(" %-55s %6s %6s" % ('-' * 55, '-----', '----')) printed = True print(" %-55s %6d %6d" % (t, delta1, delta2)) s1 += delta1 s2 += delta2 if printed: print(" %-55s %6s %6s" % ('-' * 55, '-----', '----')) print(" %-55s %6s %6s" % ('total', s1, s2)) self.delta = None def type_or_class_title(t): module = getattr(t, '__module__', '__builtin__') if module == '__builtin__': return t.__name__ return f"{module}.{t.__name__}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/runner.py0000644000076600000240000012641115011314374022200 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test execution """ import errno import gc import io import os import pprint import queue import re import subprocess import sys import threading import time import traceback import unittest import warnings from contextlib import contextmanager from io import StringIO from pathlib import Path import zope.testrunner import zope.testrunner._doctest import zope.testrunner.coverage import zope.testrunner.debug import zope.testrunner.filter import zope.testrunner.garbagecollection import zope.testrunner.interfaces import zope.testrunner.listing import zope.testrunner.logsupport import zope.testrunner.process import zope.testrunner.profiling import zope.testrunner.selftest import zope.testrunner.shuffle import zope.testrunner.statistics import zope.testrunner.tb_format from zope.testrunner import threadsupport from zope.testrunner.find import _layer_name_cache from zope.testrunner.find import import_name from zope.testrunner.find import name_from_layer from zope.testrunner.formatter import XMLOutputFormattingWrapper from zope.testrunner.layer import EmptyLayer from zope.testrunner.layer import EmptySuite from zope.testrunner.layer import UnitTests from zope.testrunner.options import get_options from zope.testrunner.refcount import TrackRefs from .digraph import DiGraph from .util import is_jython from .util import uses_refcounts class UnexpectedSuccess(Exception): pass PYREFCOUNT_PATTERN = re.compile(r'\[[0-9]+ refs\]') class SubprocessError(Exception): """An error occurred when running a subprocess """ def __init__(self, reason, stderr): self.reason = reason self.stderr = stderr def __str__(self): return f'{self.reason}: {self.stderr}' class CanNotTearDown(Exception): "Couldn't tear down a test" class Runner: """The test runner. It is the central point of this package and responsible for finding and executing tests as well as configuring itself from the (command-line) options passed into it. .. versionchanged:: 4.8.0 Add the *warnings* keyword argument. If this is ``None`` (the default) and the user hasn't configured Python otherwise with command-line arguments or environment variables, we will enable the default warnings, including ``DeprecationWarning``, when running tests. Otherwise, it can be any string acceptable to :func:`warnings.simplefilter` and that filter will be in effect while running tests. """ def __init__(self, defaults=None, args=None, found_suites=None, options=None, script_parts=None, cwd=None, warnings=None): if defaults is None: self.defaults = [] else: self.defaults = defaults self.args = args self.found_suites = found_suites self.options = options self.script_parts = script_parts self.cwd = cwd self.failed = True if warnings is None and not sys.warnoptions: # even if DeprecationWarnings are ignored by default # print them anyway unless other warnings settings are # specified by the warnings arg or the -W python flag self.warnings = 'default' else: # here self.warnings is set either to the value passed # to the warnings args or to None. # If the user didn't pass a value self.warnings will # be None. This means that the behavior is unchanged # and depends on the values passed to -W. self.warnings = warnings self.ran = 0 self.skipped = [] self.failures = [] self.errors = [] self.import_errors = [] self.show_report = True self.do_run_tests = True self.features = [] self.tests_by_layer_name = {} def ordered_layers(self): if (self.options.processes > 1 and not self.options.resume_layer): # if we want multiple processes, we need a fake layer as first # to start spreading out layers/tests to subprocesses # but only if this is not in the subprocess yield (name_from_layer(EmptyLayer), EmptyLayer, EmptySuite()) layer_names = {layer_from_name(layer_name): layer_name for layer_name in self.tests_by_layer_name} for layer in order_by_bases(layer_names): layer_name = layer_names[layer] yield layer_name, layer, self.tests_by_layer_name[layer_name] def register_tests(self, tests): """Registers tests.""" # XXX To support multiple features that find tests this shouldn't be # an update but merge the various layers individually. self.tests_by_layer_name.update(tests) def run(self): self.configure() if self.options.fail: return True # XXX Hacky to support existing code. self.layer_name_cache = _layer_name_cache self.layer_name_cache.clear() with self._enabled_warnings(): # Enable warnings during setup so that # warnings raised on import (which we do for test # discover) can be reported. # Global setup for feature in self.features: feature.global_setup() # Late setup # # Some system tools like profilers are really bad with stack # frames. E.g. hotshot doesn't like it when we leave the stack # frame that we called start() from. for feature in self.features: feature.late_setup() try: if self.do_run_tests: self.run_tests() finally: # Early teardown for feature in reversed(self.features): feature.early_teardown() # Global teardown for feature in reversed(self.features): feature.global_teardown() if self.show_report: for feature in self.features: feature.report() if self.options.xmlOutput: self.options.output.writeXMLReports() def configure(self): if self.args is None: self.args = sys.argv[:] # Check to see if we are being run as a subprocess. If we are, # then use the resume-layer and defaults passed in. if len(self.args) > 1 and self.args[1] == '--resume-layer': self.args.pop(1) resume_layer = self.args.pop(1) resume_number = int(self.args.pop(1)) self.defaults = [] while len(self.args) > 1 and self.args[1] == '--default': self.args.pop(1) self.defaults.append(self.args.pop(1)) sys.stdin = FakeInputContinueGenerator() else: resume_layer = resume_number = None options = get_options(self.args, self.defaults) options.testrunner_defaults = self.defaults options.resume_layer = resume_layer options.resume_number = resume_number if options.xmlOutput: folder = Path(options.xmlOutput).resolve() folder.mkdir(parents=True, exist_ok=True) options.output = XMLOutputFormattingWrapper( options.output, folder=folder) self.options = options self.features.append(zope.testrunner.selftest.SelfTest(self)) self.features.append(zope.testrunner.logsupport.Logging(self)) self.features.append(zope.testrunner.coverage.Coverage(self)) self.features.append(zope.testrunner._doctest.DocTest(self)) self.features.append(zope.testrunner.profiling.Profiling(self)) if is_jython: # Jython GC support is not yet implemented pass else: self.features.append( zope.testrunner.garbagecollection.Threshold(self)) self.features.append( zope.testrunner.garbagecollection.Debug(self)) self.features.append(zope.testrunner.find.Find(self)) self.features.append(zope.testrunner.shuffle.Shuffle(self)) self.features.append(zope.testrunner.process.SubProcess(self)) self.features.append(zope.testrunner.filter.Filter(self)) self.features.append(zope.testrunner.listing.Listing(self)) self.features.append( zope.testrunner.statistics.Statistics(self)) self.features.append(zope.testrunner.tb_format.Traceback(self)) # Remove all features that aren't activated self.features = [f for f in self.features if f.active] @contextmanager def _enabled_warnings(self): """ A context manager to enable warnings as configured. """ with warnings.catch_warnings(): if self.warnings: # if self.warnings is set, use it to filter all the warnings warnings.simplefilter(self.warnings) # if the filter is 'default' or 'always', special-case the # warnings from the deprecated unittest methods to show them # no more than once per module, because they can be fairly # noisy. The -Wd and -Wa flags can be used to bypass this # only when self.warnings is None. if self.warnings in ['default', 'always']: warnings.filterwarnings( 'module', category=DeprecationWarning, message=r'Please use assert\w+ instead.') yield def run_tests(self): """Run all tests that were registered. Returns True if there where failures or False if all tests passed. """ setup_layers = {} layers_to_run = list(self.ordered_layers()) should_resume = False while layers_to_run: layer_name, layer, tests = layers_to_run[0] for feature in self.features: feature.layer_setup(layer) try: self.ran += run_layer(self.options, layer_name, layer, tests, setup_layers, self.failures, self.errors, self.skipped, self.import_errors) except zope.testrunner.interfaces.EndRun: self.failed = True break except CanNotTearDown: if not self.options.resume_layer: should_resume = True break layers_to_run.pop(0) if self.options.processes > 1: should_resume = True break if self.options.stop_on_error and (self.failures or self.errors): break if should_resume: if layers_to_run: self.ran += resume_tests( self.script_parts, self.options, self.features, layers_to_run, self.failures, self.errors, self.skipped, self.cwd) if setup_layers: if self.options.resume_layer is None: self.options.output.info("Tearing down left over layers:") tear_down_unneeded( self.options, (), setup_layers, self.errors, optional=True) self.failed = bool(self.import_errors or self.failures or self.errors) def handle_layer_failure(failure_type, output, errors): if hasattr(output, 'layer_failure'): output.layer_failure(failure_type.subunit_label, sys.exc_info()) else: f = StringIO() traceback.print_exc(file=f) output.error(f.getvalue()) errors.append((failure_type, sys.exc_info())) def run_tests(options, tests, name, failures, errors, skipped, import_errors): repeat = options.repeat or 1 repeat_range = iter(range(repeat)) ran = 0 output = options.output if is_jython: # Jython has no GC suppport - set count to 0 lgarbage = 0 else: gc.collect() lgarbage = len(gc.garbage) if options.report_refcounts: if options.verbose: # XXX This code path is untested track = TrackRefs() rc = sys.gettotalrefcount() for iteration in repeat_range: if repeat > 1: output.info("Iteration %d" % (iteration + 1)) if options.verbose > 0 or options.progress: output.info(' Running:') result = TestResult(options, tests, layer_name=name) t = time.time() if options.post_mortem: # post-mortem debugging for test in tests: if result.shouldStop: break expecting_failure = ( getattr(test, "__unittest_expecting_failure__", False) or getattr( getattr(test, test._testMethodName), "__unittest_expecting_failure__", False) ) result.startTest(test) state = test.__dict__.copy() try: try: test.debug() except KeyboardInterrupt: raise except unittest.SkipTest as e: result.addSkip(test, str(e)) except BaseException: exc_info = ( sys.exc_info()[:2] + (sys.exc_info()[2].tb_next, ) ) if expecting_failure: result.addExpectedFailure(test, exc_info) else: result.addError(test, exc_info) else: if expecting_failure: result.addUnexpectedSuccess(test) else: result.addSuccess(test) finally: result.stopTest(test) test.__dict__.clear() test.__dict__.update(state) else: # normal for test in tests: if result.shouldStop: break state = test.__dict__.copy() test(result) test.__dict__.clear() test.__dict__.update(state) t = time.time() - t output.stop_tests() failures.extend(result.failures) n_failures = len(result.failures) failures.extend([(s, None) for s in result.unexpectedSuccesses]) n_failures += len(result.unexpectedSuccesses) skipped.extend(result.skipped) errors.extend(result.errors) output.summary(n_tests=result.testsRun, n_failures=n_failures, n_errors=len(result.errors) + len(import_errors), n_seconds=t, n_skipped=len(result.skipped)) ran = result.testsRun if is_jython: lgarbage = 0 else: gc.collect() if len(gc.garbage) > lgarbage: output.garbage(gc.garbage[lgarbage:]) lgarbage = len(gc.garbage) if options.report_refcounts: # If we are being tested, we don't want stdout itself to # foul up the numbers. :) try: sys.stdout.getvalue() except AttributeError: pass prev = rc rc = sys.gettotalrefcount() if options.verbose: track.update() if iteration > 0: output.detailed_refcounts(track, rc, prev) else: track.delta = None elif iteration > 0: output.refcounts(rc, prev) return ran def run_layer(options, layer_name, layer, tests, setup_layers, failures, errors, skipped, import_errors): output = options.output gathered = [] gather_layers(layer, gathered) needed = {ly: 1 for ly in gathered} if options.resume_number != 0: output.info("Running %s tests:" % layer_name) tear_down_unneeded(options, needed, setup_layers, errors) if options.resume_layer is not None: output.info_suboptimal(" Running in a subprocess.") try: setup_layer(options, layer, setup_layers) except zope.testrunner.interfaces.EndRun: raise except MemoryError: raise except Exception: handle_layer_failure(SetUpLayerFailure(layer), output, errors) return 0 else: return run_tests(options, tests, layer_name, failures, errors, skipped, import_errors) class SetUpLayerFailure(unittest.TestCase): subunit_label = 'setUp' def __init__(self, layer): super().__init__() self.layer = layer def runTest(self): pass def __str__(self): return "Layer: %s.setUp" % (name_from_layer(self.layer)) class TearDownLayerFailure(unittest.TestCase): subunit_label = 'tearDown' def __init__(self, layer): super().__init__() self.layer = layer def runTest(self): pass def __str__(self): return "Layer: %s.tearDown" % (name_from_layer(self.layer)) def spawn_layer_in_subprocess(result, script_parts, options, features, layer_name, layer, failures, errors, skipped, resume_number, cwd=None): output = options.output child = None try: # BBB if script_parts is None: script_parts = zope.testrunner._script_parts() args = [sys.executable] args.extend(script_parts) args.extend(['--resume-layer', layer_name, str(resume_number)]) for d in options.testrunner_defaults: args.extend(['--default', d]) args.extend(options.original_testrunner_args[1:]) debugargs = args # save them before messing up for windows if sys.platform.startswith('win'): args = args[0] + ' ' + ' '.join([ ('"' + a.replace('\\', '\\\\').replace('"', '\\"') + '"') for a in args[1:]]) for feature in features: feature.layer_setup(layer) child = subprocess.Popen( args, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, close_fds=not sys.platform.startswith('win')) def reader_thread(f, buf): buf.append(f.read()) # Start reading stderr in a thread. This means we don't hang if the # subprocess writes more to stderr than the pipe capacity. stderr_buf = [] stderr_thread = threading.Thread( target=reader_thread, args=(child.stderr, stderr_buf)) stderr_thread.daemon = True stderr_thread.start() while True: try: while True: # We use readline() instead of iterating over stdout # because it appears that iterating over stdout causes a # lot more buffering to take place (probably so it can # return its lines as a batch). We don't want too much # buffering because this foils automatic and human monitors # trying to verify that the subprocess is still alive. line = child.stdout.readline() if not line: break result.write(line) except OSError as e: if e.errno == errno.EINTR: # If the subprocess dies before we finish reading its # output, a SIGCHLD signal can interrupt the reading. # The correct thing to to in that case is to retry. continue output.error( "Error reading subprocess output for %s" % layer_name) output.info(str(e)) else: break # Now we should be able to finish reading stderr. stderr_thread.join() errlines = stderr_buf[0].splitlines() erriter = iter(errlines) nfail = nerr = 0 for line in erriter: try: result.num_ran, nfail, nerr = map(int, line.strip().split()) except ValueError: continue else: break else: errmsg = "Could not communicate with subprocess!" errors.append(("subprocess for %s" % layer_name, None)) if options.verbose >= 1: errmsg += "\nChild command line: %s" % debugargs if (options.verbose >= 2 or (options.verbose == 1 and len(errlines) < 20)): errmsg += ("\nChild stderr was:\n" + "\n".join(" " + line.decode('utf-8', 'replace') for line in errlines)) elif options.verbose >= 1: errmsg += ("\nChild stderr was:\n" + "\n".join(" " + line.decode('utf-8', 'replace') for line in errlines[:10]) + "\n...\n" + "\n".join(" " + line.decode('utf-8', 'replace') for line in errlines[-10:])) output.error_with_banner(errmsg) while nfail > 0: nfail -= 1 # Doing erriter.next().strip() confuses the 2to3 fixer, so # we need to do it on a separate line. Also, in python 3 this # returns bytes, so we decode it. next_fail = next(erriter) failures.append((next_fail.strip().decode(), None)) while nerr > 0: nerr -= 1 # Doing erriter.next().strip() confuses the 2to3 fixer, so # we need to do it on a separate line. Also, in python 3 this # returns bytes, so we decode it. next_err = next(erriter) errors.append((next_err.strip().decode(), None)) finally: result.done = True if child is not None: # Regardless of whether the process ran to completion, we # must properly cleanup the process to avoid # `ResourceWarning: subprocess XXX is still alive` and # `ResourceWarning: unclosed file` for its stdout and # stderr. child.kill() child.communicate() def _get_output_buffer(stream): """Get a binary-safe version of a stream.""" try: fileno = stream.fileno() except (io.UnsupportedOperation, AttributeError): pass else: # Win32 mangles \r\n to \n and that breaks streams. See # https://bugs.launchpad.net/bugs/505078. if sys.platform == 'win32': import msvcrt msvcrt.setmode(fileno, os.O_BINARY) try: stream.write(b'') except TypeError: return stream.buffer return stream class AbstractSubprocessResult: """A result of a subprocess layer run.""" num_ran = 0 done = False def __init__(self, layer_name, queue): self.layer_name = layer_name self.queue = queue self.stdout = [] def write(self, out): """Receive a line of the subprocess out.""" class DeferredSubprocessResult(AbstractSubprocessResult): """Keeps stdout around for later processing,""" def write(self, out): if not _is_dots(out): self.stdout.append(out) class ImmediateSubprocessResult(AbstractSubprocessResult): """Sends complete output to queue.""" def __init__(self, layer_name, queue): super().__init__(layer_name, queue) self.stream = _get_output_buffer(sys.stdout) def write(self, out): self.stream.write(out) # Help keep-alive monitors (human or automated) keep up-to-date. self.stream.flush() _is_dots = re.compile(br'\.+(\r\n?|\n)').match # Windows sneaks in a \r\n. class KeepaliveSubprocessResult(AbstractSubprocessResult): "Keeps stdout for later processing; sends marks to queue to show activity." _done = False def _set_done(self, value): self._done = value assert value, 'Internal error: unexpectedly setting done to False' self.queue.put((self.layer_name, ' LAYER FINISHED')) done = property(lambda self: self._done, _set_done) def write(self, out): if _is_dots(out): self.queue.put((self.layer_name, out.strip())) else: self.stdout.append(out) def resume_tests(script_parts, options, features, layers, failures, errors, skipped, cwd=None): results = [] stdout_queue = None if options.processes == 1: result_factory = ImmediateSubprocessResult elif (options.verbose > 1 and not options.subunit and not options.subunit_v2): result_factory = KeepaliveSubprocessResult stdout_queue = queue.Queue() else: result_factory = DeferredSubprocessResult resume_number = int(options.processes > 1) ready_threads = [] for layer_name, layer, tests in layers: result = result_factory(layer_name, stdout_queue) results.append(result) ready_threads.append(threading.Thread( target=spawn_layer_in_subprocess, args=(result, script_parts, options, features, layer_name, layer, failures, errors, skipped, resume_number, cwd))) resume_number += 1 # Now start a few threads at a time. running_threads = [] results_iter = iter(results) current_result = next(results_iter) last_layer_intermediate_output = None output = None # Get an object that (only) accepts bytes stdout = _get_output_buffer(sys.stdout) while ready_threads or running_threads: while len(running_threads) < options.processes and ready_threads: thread = ready_threads.pop(0) thread.start() running_threads.append(thread) for index, thread in reversed(list(enumerate(running_threads))): if not thread.is_alive(): del running_threads[index] # Clear out any messages in queue while stdout_queue is not None: previous_output = output try: layer_name, output = stdout_queue.get(False) except queue.Empty: break if layer_name != last_layer_intermediate_output: # Clarify what layer is reporting activity. if previous_output is not None: stdout.write(b']\n') stdout.write( ('[Parallel tests running in ' '%s:\n ' % (layer_name,)).encode('utf-8')) last_layer_intermediate_output = layer_name if not isinstance(output, bytes): output = output.encode('utf-8') stdout.write(output) # Display results in the order they would have been displayed, had the # work not been done in parallel. while current_result and current_result.done: if output is not None: stdout.write(b']\n') output = None stdout.writelines(current_result.stdout) try: current_result = next(results_iter) except StopIteration: current_result = None # Help keep-alive monitors (human or automated) keep up-to-date. stdout.flush() time.sleep(0.01) # Keep the loop from being too tight. # Return the total number of tests run. return sum(r.num_ran for r in results) def tear_down_unneeded(options, needed, setup_layers, errors, optional=False): # Tear down any layers not needed for these tests. The unneeded layers # might interfere. unneeded = [layer for layer in setup_layers if layer not in needed] unneeded = order_by_bases(unneeded) unneeded.reverse() output = options.output for layer in unneeded: output.start_tear_down(name_from_layer(layer)) t = time.time() try: try: if hasattr(layer, 'tearDown'): layer.tearDown() except NotImplementedError: output.tear_down_not_supported() if not optional: raise CanNotTearDown(layer) except MemoryError: raise except Exception: handle_layer_failure( TearDownLayerFailure(layer), output, errors) else: output.stop_tear_down(time.time() - t) finally: del setup_layers[layer] cant_pm_in_subprocess_message = """ Can't post-mortem debug when running a layer as a subprocess! Try running layer %r by itself. """ def setup_layer(options, layer, setup_layers): assert layer is not object output = options.output if layer not in setup_layers: for base in layer.__bases__: if base is not object: setup_layer(options, base, setup_layers) output.start_set_up(name_from_layer(layer)) t = time.time() if hasattr(layer, 'setUp'): try: layer.setUp() except MemoryError: raise except Exception: if options.post_mortem: if options.resume_layer: options.output.error_with_banner( cant_pm_in_subprocess_message % options.resume_layer) raise else: zope.testrunner.debug.post_mortem( sys.exc_info()) else: raise output.stop_set_up(time.time() - t) setup_layers[layer] = 1 class TestResult(unittest.TestResult): def __init__(self, options, tests, layer_name=None): unittest.TestResult.__init__(self) self.options = options # Calculate our list of relevant layers we need to call testSetUp # and testTearDown on. layers = [] gather_layers(layer_from_name(layer_name), layers) self.layers = order_by_bases(layers) count = 0 for test in tests: count += test.countTestCases() self.count = count self._stdout_buffer = None self._stderr_buffer = None self._original_stdout = sys.stdout self._original_stderr = sys.stderr def testSetUp(self): """A layer may define a setup method to be called before each individual test. """ for layer in self.layers: if hasattr(layer, 'testSetUp'): layer.testSetUp() def testTearDown(self): """A layer may define a teardown method to be called after each individual test. This is useful for clearing the state of global resources or resetting external systems such as relational databases or daemons. """ for layer in self.layers[-1::-1]: if hasattr(layer, 'testTearDown'): layer.testTearDown() def _makeBufferedStdStream(self): """Make a buffered stream to replace a standard stream.""" # The returned stream needs to have a 'buffer' attribute, since # some tests may expect that to exist on standard streams, and a # 'getvalue' method for the convenience of _restoreStdStreams. # This requires some care. class BufferedStandardStream(io.TextIOWrapper): def getvalue(self): return self.buffer.getvalue().decode( encoding=self.encoding, errors=self.errors) return BufferedStandardStream( io.BytesIO(), newline='\n', write_through=True) def _setUpStdStreams(self): """Set up buffered standard streams, if requested.""" if self.options.buffer: if self._stdout_buffer is None: self._stdout_buffer = self._makeBufferedStdStream() if self._stderr_buffer is None: self._stderr_buffer = self._makeBufferedStdStream() sys.stdout = self._stdout_buffer sys.stderr = self._stderr_buffer def _restoreStdStreams(self): """Restore the buffered standard streams and return any contents.""" if self.options.buffer: stdout = sys.stdout.getvalue() stderr = sys.stderr.getvalue() sys.stdout = self._original_stdout sys.stderr = self._original_stderr self._stdout_buffer.seek(0) self._stdout_buffer.truncate(0) self._stderr_buffer.seek(0) self._stderr_buffer.truncate(0) return stdout, stderr else: return None, None def startTest(self, test): self._test_state = test.__dict__.copy() self.testSetUp() unittest.TestResult.startTest(self, test) testsRun = self.testsRun - 1 # subtract the one the base class added count = test.countTestCases() self.testsRun = testsRun + count self.options.output.start_test(test, self.testsRun, self.count) self._threads = threadsupport.enumerate() self._start_time = time.time() self._setUpStdStreams() def addSuccess(self, test): self._restoreStdStreams() t = max(time.time() - self._start_time, 0.0) self.options.output.test_success(test, t) def addSkip(self, test, reason): if not hasattr(self, "_test_state"): # ``startTest`` was not called -- set up extected state self._test_state = test.__dict__ count = test.countTestCases() self.testsRun += count self.options.output.start_test(test, self.testsRun, self.count) self._threads = threadsupport.enumerate() if not hasattr(self, "_start_time"): self._start_time = time.time() else: self._restoreStdStreams() unittest.TestResult.addSkip(self, test, reason) self.options.output.test_skipped(test, reason) def addSubTest(self, test, subtest, exc_info): if exc_info is None: return stdout, stderr = self._restoreStdStreams() outp = self.options.output report = (outp.test_failure if issubclass(exc_info[0], test.failureException) else outp.test_error) report(subtest, time.time() - self._start_time, exc_info, stdout=stdout, stderr=stderr) unittest.TestResult.addSubTest(self, test, subtest, exc_info) if self.options.post_mortem: if self.options.resume_layer: self.options.output.error_with_banner( "Can't post-mortem debug when running a layer as" " a subprocess!") else: zope.testrunner.debug.post_mortem(exc_info) elif self.options.stop_on_error: self.stop() def addError(self, test, exc_info): stdout, stderr = self._restoreStdStreams() self.options.output.test_error(test, time.time() - self._start_time, exc_info, stdout=stdout, stderr=stderr) unittest.TestResult.addError(self, test, exc_info) if self.options.post_mortem: if self.options.resume_layer: self.options.output.error_with_banner("Can't post-mortem debug" " when running a layer" " as a subprocess!") else: zope.testrunner.debug.post_mortem(exc_info) elif self.options.stop_on_error: self.stop() def addFailure(self, test, exc_info): stdout, stderr = self._restoreStdStreams() self.options.output.test_failure(test, time.time() - self._start_time, exc_info, stdout=stdout, stderr=stderr) unittest.TestResult.addFailure(self, test, exc_info) if self.options.post_mortem: # XXX: mgedmin: why isn't there a resume_layer check here like # in addError? zope.testrunner.debug.post_mortem(exc_info) elif self.options.stop_on_error: self.stop() def addExpectedFailure(self, test, exc_info): self._restoreStdStreams() t = max(time.time() - self._start_time, 0.0) self.options.output.test_success(test, t) unittest.TestResult.addExpectedFailure(self, test, exc_info) def addUnexpectedSuccess(self, test): stdout, stderr = self._restoreStdStreams() self.options.output.test_error( test, time.time() - self._start_time, (UnexpectedSuccess, UnexpectedSuccess(), None), stdout=stdout, stderr=stderr) unittest.TestResult.addUnexpectedSuccess(self, test) if self.options.post_mortem: if self.options.resume_layer: self.options.output.error_with_banner("Can't post-mortem debug" " when running a layer" " as a subprocess!") else: self.options.output.error_with_banner("Can't post-mortem debug" " an unexpected success") elif self.options.stop_on_error: self.stop() def stopTest(self, test): self.testTearDown() # Without clearing, cyclic garbage referenced by the test # would be reported in the following test. test.__dict__.clear() test.__dict__.update(self._test_state) del self._test_state cycles = None if (uses_refcounts and self.options.gc_after_test and self.options.verbose >= 4): gc_opts = gc.get_debug() gc.set_debug(gc.DEBUG_SAVEALL) gc.collect() if gc.garbage: g = DiGraph(gc.garbage) for obj in gc.garbage: g.add_neighbors(obj, gc.get_referents(obj)) cycles = [[repr_lines(o) for o in c] for c in g.sccs()] del gc.garbage[:] g = obj = None # avoid to hold cyclic garbage gc.set_debug(gc_opts) gccount = gc.collect() \ if uses_refcounts and self.options.gc_after_test else 0 self.options.output.stop_test(test, gccount) if cycles: self.options.output.test_cycles(test, cycles) if is_jython: pass else: if gc.garbage: self.options.output.test_garbage(test, gc.garbage) # TODO: Perhaps eat the garbage here, so that the garbage isn't # printed for every subsequent test. # Did the test leave any new threads behind? new_threads = [] for t in threadsupport.enumerate(): if t.is_alive() and t not in self._threads: if not any([re.match(p, t.name) for p in self.options.ignore_new_threads]): new_threads.append(t) if new_threads: self.options.output.test_threads(test, new_threads) def layer_from_name(layer_name): """Return the layer for the corresponding layer_name by discovering and importing the necessary module if necessary. Note that a name -> layer cache is maintained by name_from_layer to allow locating layers in cases where it would otherwise be impossible. """ if layer_name in _layer_name_cache: return _layer_name_cache[layer_name] layer_names = layer_name.split('.') layer_module, module_layer_name = layer_names[:-1], layer_names[-1] module_name = '.'.join(layer_module) module = import_name(module_name) try: return getattr(module, module_layer_name) except AttributeError: # the default error is very uninformative: # AttributeError: 'module' object has no attribute 'DemoLayer' # it doesn't say *which* module raise AttributeError('module %r has no attribute %r' % (module_name, module_layer_name)) def layer_sort_key(layer): """Compute sort key for layers. Based on the reverse MRO ordering in order to put layers with shared base layers next to each other. """ seen = set() key = [] # Note: we cannot reuse gather_layers() here because it uses a # different traversal order. binding = {} # hack to avoid recursion -- for PY2 def _gather(layer): seen.add(layer) # We make the simplifying assumption that the order of initialization # of base layers does not matter. Given that, traversing the bases # in reverse order here keeps the ordering of layers in # testrunner-layers.rst the same as it was in older versions of # zope.testrunner, so let's use that. for base in layer.__bases__[::-1]: if base is not object and base not in seen: binding["_gather"](base) key.append(layer) binding["_gather"] = _gather _gather(layer) try: return tuple(name_from_layer(ly) for ly in key if ly != UnitTests) finally: binding.clear() # break reference cycle def order_by_bases(layers): """Order the layers from least to most specific (bottom to top). Puts unit tests first. Groups layers with common base layers together. Sorts the rest alphabetically. Removes duplicates. """ layers = sorted(layers, key=layer_sort_key, reverse=True) gathered = [] for layer in layers: gather_layers(layer, gathered) gathered.reverse() seen = {} result = [] for layer in gathered: if layer not in seen: seen[layer] = 1 if layer in layers: result.append(layer) return result def gather_layers(layer, result): if layer is not object: result.append(layer) for b in layer.__bases__: gather_layers(b, result) class FakeInputContinueGenerator: def readline(self): print('c\n') print('*' * 70) print("Can't use pdb.set_trace when running a layer" " as a subprocess!") print('*' * 70) print() return 'c\n' def close(self): pass def repr_lines(obj, max_width=75, max_lines=5): """represent *obj* by a sequence of text lines. Use at most *max_lines*, each with at most *max_width* chars. """ try: oi = pprint.pformat(obj) except Exception: # unprintable oi = f"{obj.__class__} instance at 0x{id(obj):x}" # limit cmps = oi.split("\n", max_lines) if len(cmps) > max_lines: cmps[-1] = "..." for i, li in enumerate(cmps): if len(li) > max_width: cmps[i] = li[:max_width - 3] + "..." return cmps ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/selftest.py0000644000076600000240000000203015011314374022506 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Self-test support. Provides setup routines that enable the test runner to test itself. """ import pdb # noqa: T100 import for pdb found import zope.testrunner.feature real_pdb_set_trace = pdb.set_trace class SelfTest(zope.testrunner.feature.Feature): active = True def global_setup(self): # Make sure we start with real pdb.set_trace. pdb.set_trace = real_pdb_set_trace ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/shuffle.py0000644000076600000240000000603415011314374022321 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Shuffle tests discovered before executing them. """ import math import random import time import zope.testrunner.feature class Shuffle(zope.testrunner.feature.Feature): """Take the tests found so far and shuffle them.""" def __init__(self, runner): super().__init__(runner) self.active = runner.options.shuffle self.seed = runner.options.shuffle_seed if self.seed is None: # We can't rely on the random modules seed initialization because # we can't introspect the seed later for reporting. This is a # simple emulation of what random.Random.seed does anyway. self.seed = int(time.time() * 256) # use fractional seconds def global_setup(self): rng = random.Random(self.seed) # in case somebody tries to use a string as the seed rng.seed(self.seed, version=1) # Be careful to shuffle the layers in a deterministic order! for layer, suite in sorted(self.runner.tests_by_layer_name.items()): # Test suites cannot be modified through a public API. We thus # take a mutable copy of the list of tests of that suite, shuffle # that and replace the test suite instance with a new one of the # same class. tests = list(suite) # The standard library guarantees rng.random() will return the # same floats if given the same seed. It makes no guarantees # for rng.randrange, or rng.choice, or rng.shuffle. Experiments # show that Python happily breaks backwards compatibility for # these functions. We used to use `rng.shuffle(tests, # random=rng.random)` to trick it into using the old algorithm, # but even this was deprecated in Python 3.9 and removed in # 3.11. Accordingly, inline equivalent code which we know is # stable across Python versions. floor = math.floor for i in reversed(range(1, len(tests))): # Pick an element in tests[:i+1] with which to exchange # tests[i]. j = floor(rng.random() * (i + 1)) tests[i], tests[j] = tests[j], tests[i] self.runner.tests_by_layer_name[layer] = suite.__class__(tests) def report(self): msg = "Tests were shuffled using seed number %d." % self.seed self.runner.options.output.info(msg) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/statistics.py0000644000076600000240000000300615011314374023053 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test runner statistics """ import time import zope.testrunner.feature class Statistics(zope.testrunner.feature.Feature): active = True layers_run = 0 tests_run = 0 def late_setup(self): self.start_time = time.time() def early_teardown(self): self.end_time = time.time() def global_teardown(self): self.total_time = self.end_time - self.start_time def layer_setup(self, layer): self.layers_run += 1 def report(self): if not self.runner.do_run_tests: return if self.layers_run == 1: return self.runner.options.output.totals( n_tests=self.runner.ran, n_failures=len(self.runner.failures), n_errors=len(self.runner.errors) + len(self.runner.import_errors), n_seconds=self.total_time, n_skipped=len(self.runner.skipped)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tb_format.py0000644000076600000240000000614315011314374022643 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Set up testing environment """ import sys import traceback import zope.exceptions.exceptionformatter import zope.testrunner.feature try: from traceback import _parse_value_tb from traceback import _sentinel except ImportError: # before 3.10 # for Python before 3.10, the first 3 parameters of # ``print_exception`` and ``format_exception`` are all mandatory # and the first one (``etype`` alias ``t``) is ignored _sentinel = object() def _parse_value_tb(ignored, value, tb): return value, tb def _iter_chain(exc, custom_tb=None, seen=None): if seen is None: seen = set() seen.add(exc) its = [] context = exc.__context__ cause = exc.__cause__ if cause is not None and cause not in seen: its.append(_iter_chain(cause, False, seen)) its.append([(traceback._cause_message, None)]) elif (context is not None and not exc.__suppress_context__ and context not in seen): its.append(_iter_chain(context, None, seen)) its.append([(traceback._context_message, None)]) its.append([(exc, custom_tb or exc.__traceback__)]) # itertools.chain is in an extension module and may be unavailable for it in its: yield from it def format_exception(t, value=_sentinel, tb=_sentinel, limit=None, chain=None): v, tb = _parse_value_tb(t, value, tb) if chain: values = _iter_chain(v, tb) else: values = [(v, tb)] fmt = zope.exceptions.exceptionformatter.TextExceptionFormatter( limit=None, with_filenames=True) for v, tb in values: if isinstance(v, str): return v return fmt.formatException(t, v, tb) def print_exception(t, value=_sentinel, tb=_sentinel, limit=None, file=None, chain=None): v, tb = _parse_value_tb(t, value, tb) if chain: values = _iter_chain(v, tb) else: values = [(v, tb)] if file is None: file = sys.stdout for v, tb in values: file.writelines(format_exception(t, v, tb, limit)) class Traceback(zope.testrunner.feature.Feature): active = True def global_setup(self): self.old_format = traceback.format_exception traceback.format_exception = format_exception self.old_print = traceback.print_exception traceback.print_exception = print_exception def global_teardown(self): traceback.format_exception = self.old_format traceback.print_exception = self.old_print ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8606625 zope_testrunner-7.3/src/zope/testrunner/tests/0000755000076600000240000000000015011314376021454 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/__init__.py0000644000076600000240000000002215011314374023555 0ustar00m.howitzstaff# Make a package. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8608253 zope_testrunner-7.3/src/zope/testrunner/tests/logsupport/0000755000076600000240000000000015011314376023672 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/logsupport/log.ini0000644000076600000240000000036015011314374025151 0ustar00m.howitzstaff[loggers] keys=root [handlers] keys=stderr [formatters] keys=minimal [logger_root] level=DEBUG handlers=stderr [handler_stderr] level=INFO class=StreamHandler args=(sys.stderr,) formatter=minimal [formatter_minimal] format=%(message)s ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_digraph.py0000644000076600000240000000461715011314374024511 0ustar00m.howitzstafffrom unittest import TestCase from ..digraph import DiGraph class Tests(TestCase): def test_int_digraph(self): self.check(lambda nodes: DiGraph(nodes, False)) def test_obj_diraph(self): self.check(DiGraph) def check(self, digraph_factory): ints = list(range(100)) g = digraph_factory(ints) self.assertEqual(sorted(g.nodes()), ints) nb0 = [ints[0], ints[1]] g.add_neighbors(ints[0], nb0) self.assertEqual(sorted(g.neighbors(ints[0])), nb0) self.assertEqual(list(g.neighbors(ints[1])), []) # unknown neighbors with self.assertRaises(KeyError): g.add_neighbors(ints[0], (200,), False) g.add_neighbors(ints[0], (200,)) # ignored self.assertEqual(sorted(g.neighbors(ints[0])), nb0) # unknown node with self.assertRaises(KeyError): g.add_neighbors(200, (ints[0],), False) g.add_neighbors(200, (ints[0],)) # ignored # additional neighbors nb0.append(ints[2]) g.add_neighbors(ints[0], nb0) self.assertEqual(sorted(g.neighbors(ints[0])), nb0) def test_scc_linear(self): ints = list(range(100)) g = DiGraph(ints) for i in range(99): g.add_neighbors(ints[i], (ints[i + 1], )) self.assertEqual(sorted(g.sccs(True)), [[i] for i in ints]) def test_trivial_cycle(self): g = dig_from_dict({1: 2, 2: (2, 3), 3: ()}) sccs = list(g.sccs()) self.assertEqual(len(sccs), 1) cy = sccs[0] self.assertEqual(len(cy), 1) self.assertEqual(cy[0], 2) def test_forest(self): g = dig_from_dict({1: (), 2: ()}) sccs = list(g.sccs(True)) self.assertEqual(sorted(sccs), [[1], [2]]) def test_complex(self): g = dig_from_dict({0: 1, 1: 2, 2: (3, 4), 3: (0, 4), 4: (5, 2), 5: (6, 9), 6: (5, 7), 7: 8, 8: 9, 9: (5, 6)}) sccs = list(g.sccs()) self.assertEqual(len(sccs), 2) for c in sccs: self.assertEqual(len(c), 5) self.assertEqual(max(c) - min(c), 4) self.assertEqual(sorted(sccs[0] + sccs[1]), list(range(10))) def dig_from_dict(d): g = DiGraph(list(d), False) for e in d.items(): n, nbs = e if not hasattr(nbs, "__len__"): nbs = nbs, g.add_neighbors(n, nbs, False) return g ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_doctest.py0000644000076600000240000003371115011314374024535 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004-2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test harness for the test runner itself. """ import doctest import gc import io import os import re import sys import unittest from zope.testing import renormalizing from ..util import uses_refcounts # separated checkers for the different platform, # because it s...s to maintain just one if sys.platform == 'win32': checker = renormalizing.RENormalizing([ # rewrite pdb prompt for coverage runs: (re.compile('->None'), ''), (re.compile(r""), (r'?')), # testtools content formatter is used to mime-encode # tracebacks when the SubunitOutputFormatter is used, and the # resulting text includes a size which can vary depending on # the path included in the traceback. (re.compile(r'traceback\n[A-F\d]+', re.MULTILINE), r'traceback\nNNN'), # hopefully, we'll make Windows happy # replaces drives with nothing (re.compile("'[A-Za-z]:\\\\"), "'"), # more Windows happiness # double backslashes in coverage??? (re.compile(r'\\\\'), '/'), # even more Windows happiness # replaces backslashes in paths (re.compile(r'\\'), '/'), (re.compile(r'/r$', re.MULTILINE), '\\r'), # undo some of that # this is a magic to put linefeeds into the doctest (re.compile('##r##\n'), '\r'), (re.compile(r'(\d+ minutes )?\d+[.]\d\d\d seconds'), 'N.NNN seconds'), (re.compile(r'\d+[.]\d\d\d s'), 'N.NNN s'), (re.compile(r'\d+[.]\d\d\d{'), 'N.NNN{'), (re.compile(r'\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d+'), 'YYYY-MM-DD HH:MM:SS.mmmmmm'), (re.compile('( |")[^\n]+testrunner-ex'), r'\1testrunner-ex'), (re.compile('( |")[^\n]+testrunner.py'), r'\1testrunner.py'), (re.compile(r'> [^\n]*(doc|unit)test[.]py\(\d+\)'), r'\1test.py(NNN)'), (re.compile(r'[.]py\(\d+\)'), r'.py(NNN)'), (re.compile(r'[.]py:\d+'), r'.py:NNN'), (re.compile(r' line \d+,', re.IGNORECASE), r' Line NNN,'), (re.compile(r' line {([a-z]+)}\d+{', re.IGNORECASE), r' Line {\1}NNN{'), # omit traceback entries for unittest.py or doctest.py (and # their package variants) from output: (re.compile(r'^ +File "[^\n]*(doctest|unittest|case)(/__init__)?.py", ' r'[^\n]+\n[^\n]+\n', re.MULTILINE), r''), (re.compile(r'^{\w+} +File "{\w+}[^\n]*(doctest|unittest|case)' r'(/__init__)?.py{\w+}", [^\n]+\n[^\n]+\n', re.MULTILINE), r''), # (re.compile('^> [^\n]+->None$', re.M), '> ...->None'), # disregard trailing whitespace (re.compile(r'\s+$'), ''), # Python 3.13+ no longer prints this `--Return--` line in pdb tests. (re.compile(r'--Return--'), ''), ]) else: # *nix checker = renormalizing.RENormalizing([ # rewrite pdb prompt for coverage runs: (re.compile('->None'), ''), (re.compile(r""), (r'?')), # this is a magic to put linefeeds into the doctest # on win it takes one step, linux is crazy about the same... (re.compile('##r##'), r'\r'), (re.compile(r'\r'), '\\\\r\n'), (re.compile(r'(\d+ minutes )?\d+[.]\d\d\d seconds'), 'N.NNN seconds'), (re.compile(r'\d+[.]\d\d\d s'), 'N.NNN s'), (re.compile(r'\d+[.]\d\d\d{'), 'N.NNN{'), (re.compile(r'\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d+'), 'YYYY-MM-DD HH:MM:SS.mmmmmm'), (re.compile('( |"|\')[^\'\n]+testrunner-ex'), r'\1testrunner-ex'), (re.compile('( |"|\')[^\'\n]+testrunner.py'), r'\1testrunner.py'), (re.compile(r'> [^\n]*(doc|unit)test[.]py\(\d+\)'), r'\1test.py(NNN)'), (re.compile(r'[.]py\(\d+\)'), r'.py(NNN)'), (re.compile(r'[.]py:\d+'), r'.py:NNN'), (re.compile(r' line \d+,', re.IGNORECASE), r' Line NNN,'), (re.compile(r' line {([a-z]+)}\d+{', re.IGNORECASE), r' Line {\1}NNN{'), # testtools content formatter is used to mime-encode # tracebacks when the SubunitOutputFormatter is used, and the # resulting text includes a size which can vary depending on # the path included in the traceback. (re.compile(r'traceback\n[A-F\d]+', re.MULTILINE), r'traceback\nNNN'), # omit traceback entries for unittest.py or doctest.py (and # their package variants) from output: (re.compile(r'^ +File "[^\n]*(doctest|unittest|case)(/__init__)?.py", ' r'[^\n]+\n[^\n]+\n', re.MULTILINE), r''), (re.compile(r'^{\w+} +File "{\w+}[^\n]*(doctest|unittest|case)' r'(/__init__)?.py{\w+}", [^\n]+\n[^\n]+\n', re.MULTILINE), r''), # Python 3.13+ no longer prints this `--Return--` line in pdb tests. (re.compile(r'--Return--'), ''), # disregard trailing whitespace (re.compile(r'\s+$', re.MULTILINE), ''), ]) # Monkey-patch doctest with our own _SpoofOut replacement. We # need sys.stdout to be binary-capable so that we can pass through binary # output from test formatters cleanly, which is in particular required for # subunit. We don't expect to be able to do actual binary comparisons in # doctests, but that's OK. # See https://github.com/zopefoundation/zope.testrunner/pull/23 for the # background. class _SpoofOut(io.TextIOWrapper): def __init__(self): super().__init__(io.BytesIO(), encoding='utf-8') def write(self, s): super().write(s) # Always flush immediately so that getvalue() never returns # short results. self.flush() def getvalue(self): result = self.buffer.getvalue().decode('utf-8', 'replace') # If anything at all was written, make sure there's a trailing # newline. There's no way for the expected output to indicate # that a trailing newline is missing. if result and not result.endswith("\n"): result += "\n" # We're reading bytes, so we have to do universal newlines # conversion by hand. return result.replace(os.linesep, '\n') def truncate(self, size=None): self.seek(size) super().truncate() def setUp(test): test.globs['saved-sys-info'] = ( sys.path[:], sys.argv[:], sys.modules.copy(), ) if hasattr(gc, 'get_threshold'): test.globs['saved-gc-threshold'] = gc.get_threshold() test.globs['saved-doctest-SpoofOut'] = doctest._SpoofOut doctest._SpoofOut = _SpoofOut test.globs['this_directory'] = os.path.split(__file__)[0] test.globs['testrunner_script'] = sys.argv[0] def tearDown(test): sys.path[:], sys.argv[:] = test.globs['saved-sys-info'][:2] if hasattr(gc, 'get_threshold'): gc.set_threshold(*test.globs['saved-gc-threshold']) sys.modules.clear() sys.modules.update(test.globs['saved-sys-info'][2]) doctest._SpoofOut = test.globs['saved-doctest-SpoofOut'] def test_suite(): optionflags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF) suites = [ doctest.DocFileSuite( 'testrunner-arguments.rst', 'testrunner-coverage.rst', 'testrunner-debugging-layer-setup.rst', 'testrunner-debugging-import-failure.rst', 'testrunner-debugging-nonprintable-exc.rst', 'testrunner-debugging.rst', 'testrunner-edge-cases.rst', 'testrunner-errors.rst', 'testrunner-layers-api.rst', 'testrunner-layers-instances.rst', 'testrunner-layers-buff.rst', 'testrunner-subprocess-errors.rst', 'testrunner-layers-cantfind.rst', 'testrunner-layers-cwd.rst', 'testrunner-layers-ntd.rst', 'testrunner-layers-topological-sort.rst', 'testrunner-layers.rst', 'testrunner-progress.rst', 'testrunner-colors.rst', 'testrunner-simple.rst', 'testrunner-nestedcode.rst', 'testrunner-test-selection.rst', 'testrunner-verbose.rst', 'testrunner-repeat.rst', 'testrunner-knit.rst', 'testrunner-shuffle.rst', 'testrunner-stops-when-stop-on-error.rst', 'testrunner-new-threads.rst', 'testrunner-subtest.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker), doctest.DocTestSuite('zope.testrunner'), doctest.DocTestSuite('zope.testrunner.coverage', optionflags=optionflags), doctest.DocTestSuite('zope.testrunner.options'), doctest.DocTestSuite('zope.testrunner.find'), ] # PyPy uses a different garbage collector if hasattr(gc, 'get_threshold'): suites.append( doctest.DocFileSuite( 'testrunner-gc.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) # PyPy does not support sourceless imports, apparently (tried version 1.9) if 'PyPy' not in sys.version and not sys.dont_write_bytecode: suites.append( doctest.DocFileSuite( 'testrunner-wo-source.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) if sys.platform == 'win32': suites.append( doctest.DocFileSuite( 'testrunner-coverage-win32.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) suites.append( doctest.DocFileSuite( 'testrunner-profiling.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=renormalizing.RENormalizing([ (re.compile(r'tests_profile[.]\S*[.]prof'), 'tests_profile.*.prof'), ]), ) ) suites.append( doctest.DocFileSuite( 'testrunner-profiling-cprofiler.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=renormalizing.RENormalizing([ (re.compile(r'tests_profile[.]\S*[.]prof'), 'tests_profile.*.prof'), ]), ) ) suites.append( doctest.DocFileSuite( 'testrunner-report-skipped.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker) ) if hasattr(sys, 'gettotalrefcount'): suites.append( doctest.DocFileSuite( 'testrunner-leaks.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=renormalizing.RENormalizing([ (re.compile(r'(\d+ minutes )?\d+[.]\d\d\d seconds'), 'N.NNN seconds'), (re.compile(r'sys refcount=\d+ +change=\d+'), 'sys refcount=NNNNNN change=NN'), (re.compile(r'sum detail refcount=\d+ +'), 'sum detail refcount=NNNNNN '), (re.compile(r'total +\d+ +\d+'), 'total NNNN NNNN'), (re.compile(r"^ +(int|type) +-?\d+ +-?\d+ *\n", re.M), ''), ]), ) ) else: suites.append( doctest.DocFileSuite( 'testrunner-leaks-err.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker, ) ) if uses_refcounts: suites.append( doctest.DocFileSuite( 'testrunner-gc-after-test.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=renormalizing.RENormalizing([ (re.compile(r'(\d+ minutes )?\d+[.]\d\d\d seconds'), 'N.NNN seconds'), (re.compile(r'\(\d+[.]\d\d\d s\)'), '(N.NNN s)'), # objects on cycle differ between different python versions (re.compile(r'\[\d+\]'), '[C]')]))) try: import subunit subunit except ImportError: suites.append( doctest.DocFileSuite( 'testrunner-subunit-err.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) else: suites.append( doctest.DocFileSuite( 'testrunner-subunit.rst', 'testrunner-subunit-v2.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) if hasattr(sys, 'gettotalrefcount'): suites.append( doctest.DocFileSuite( 'testrunner-subunit-leaks.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) suites.append(doctest.DocFileSuite( 'testrunner-expected-failures.rst', setUp=setUp, tearDown=tearDown, optionflags=optionflags, checker=checker)) return unittest.TestSuite(suites) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_filter.py0000644000076600000240000000545715011314374024363 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2010 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Unit tests for the testrunner's filtering functions """ import unittest from zope.testrunner import filter class TestFilterMakerFunction(unittest.TestCase): def test_no_filters(self): accept = filter.build_filtering_func([]) self.assertFalse(accept('test_xx')) self.assertFalse(accept('test_yy')) self.assertFalse(accept('test_zz')) def test_match_all_filter(self): accept = filter.build_filtering_func(['.']) self.assertTrue(accept('test_xx')) self.assertTrue(accept('test_yy')) self.assertTrue(accept('test_zz')) def test_select_specific_pattern(self): accept = filter.build_filtering_func(['xx']) self.assertTrue(accept('test_xx')) self.assertFalse(accept('test_yy')) self.assertFalse(accept('test_zz')) def test_select_several_patterns(self): accept = filter.build_filtering_func(['xx', 'yy']) self.assertTrue(accept('test_xx')) self.assertTrue(accept('test_yy')) self.assertFalse(accept('test_zz')) def test_reject_one_pattern(self): accept = filter.build_filtering_func(['!xx']) self.assertFalse(accept('test_xx')) self.assertTrue(accept('test_yy')) self.assertTrue(accept('test_zz')) def test_reject_several_patterns(self): accept = filter.build_filtering_func(['!xx', '!zz']) self.assertFalse(accept('test_xx')) self.assertTrue(accept('test_yy')) self.assertFalse(accept('test_zz')) def test_accept_and_reject(self): accept = filter.build_filtering_func(['!xx', 'yy']) self.assertFalse(accept('test_xx')) self.assertTrue(accept('test_yy')) self.assertFalse(accept('test_zz')) def test_accept_and_reject_overlap(self): accept = filter.build_filtering_func(['!test_', 'yy']) self.assertFalse(accept('test_xx')) self.assertFalse(accept('test_yy')) self.assertFalse(accept('test_zz')) def test_accept_and_reject_same(self): accept = filter.build_filtering_func(['yy', '!yy']) self.assertFalse(accept('test_xx')) self.assertFalse(accept('test_yy')) self.assertFalse(accept('test_zz')) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_find.py0000644000076600000240000000742215011314374024010 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2017 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Unit tests for test discovery.""" import doctest import os.path import unittest from zope.testrunner import find class UniquenessOptions: """A basic mock of our command-line options.""" keepbytecode = True at_level = 99 only_level = None test = [] module = [] require_unique_ids = True class TestUniqueness(unittest.TestCase): """Test how the testrunner handles non-unique IDs.""" def setUp(self): super().setUp() suites = [ doctest.DocFileSuite('testrunner-ex/sampletests.rst'), doctest.DocFileSuite('testrunner-ex/sampletests.rst'), doctest.DocFileSuite('testrunner-ex/sampletestsl.rst'), doctest.DocFileSuite('testrunner-ex/sampletestsl.rst'), ] self.test_suites = unittest.TestSuite(suites) def test_tests_from_suite_records_duplicate_test_ids(self): # If tests_from_suite encounters a test ID which has already been # registered, it records them in the duplicated_test_ids set it is # passed. duplicated_test_ids = set() list(find.tests_from_suite( self.test_suites, UniquenessOptions(), duplicated_test_ids=duplicated_test_ids)) self.assertNotEqual(0, len(duplicated_test_ids)) def test_tests_from_suite_ignores_duplicate_ids_if_option_not_set(self): # If the require_unique_ids option is not set, tests_from_suite will # not record any of the IDs as duplicates. options = UniquenessOptions() options.require_unique_ids = False duplicated_test_ids = set() list(find.tests_from_suite( self.test_suites, options, duplicated_test_ids=duplicated_test_ids)) self.assertEqual(0, len(duplicated_test_ids)) def test_find_tests_raises_error_if_duplicates_found(self): # If find_tests, which calls tests_from_suite, finds a duplicate # test ID, it raises an error. self.assertRaises( find.DuplicateTestIDError, find.find_tests, UniquenessOptions(), [self.test_suites]) def test_DuplicateTestIDError_message_contains_all_test_ids(self): # The error raised by find_tests when duplicates are encountered # contains all the duplicate test IDs it found. with self.assertRaises(find.DuplicateTestIDError) as e: find.find_tests(UniquenessOptions(), [self.test_suites]) self.assertIn( os.path.join('testrunner-ex', 'sampletests.rst'), str(e.exception)) self.assertIn( os.path.join('testrunner-ex', 'sampletestsl.rst'), str(e.exception)) class TestIdentifierMatches(unittest.TestCase): """Test which folders are ignored by the test runner.""" def test_ignored(self): folders = ('spaces are bad', 'dashes-bad', 'ünicodeLeading') for name in folders: self.assertFalse(find.identifier(name), f'{name} is accepted') def test_accepted(self): folders = ( 'random', 'CamelCase', 'Under_scores', '_leading_scores', 'unicöde' ) for name in folders: self.assertTrue(find.identifier(name), f'{name} is not accepted') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_logsupport.py0000644000076600000240000000403215011314374025300 0ustar00m.howitzstafffrom logging import DEBUG from logging import getLogger from os import chdir from os import environ from os import getcwd from os.path import dirname from os.path import join from unittest import TestCase from warnings import catch_warnings from ..logsupport import Logging setup_logging = Logging.global_setup setup_logging = getattr(setup_logging, "__func__", setup_logging) evn = "ZOPE_TESTRUNNER_LOG_INI" class LogsupportTests(TestCase): def setUp(self): # save root logger config logger = getLogger() self.logconfig = lc = logger.__dict__.copy() for n in ("filters", "handlers"): lc[n] = lc[n][:] # copy del getattr(logger, n)[:] # clear # save working directory self.cwd = getcwd() # save and clear envvar self.envvar = ev = environ.get(evn, self) if ev is not self: del environ[evn] def tearDown(self): # restore working directory chdir(self.cwd) # restore root logger configuration logger = getLogger() logger.__dict__.update(self.logconfig) # restore envvar ev = self.envvar if ev is not self: environ[evn] = ev elif evn in environ: del environ[evn] def test_via_cwd(self): chdir(join(dirname(__file__), "logsupport")) self.check_logging() def test_via_envvar(self): lc = join(dirname(__file__), "logsupport", "log.ini") environ[evn] = lc self.check_logging() def test_via_bad_envvar(self): chdir(join(dirname(__file__), "logsupport")) environ[evn] = "not_existing" with catch_warnings(record=True) as w: self.check_logging() self.assertEqual(len(w), 1) def check_logging(self): setup_logging(None) logger = getLogger() self.assertEqual(logger.level, DEBUG) self.assertEqual(len(logger.handlers), 1) lc = join(dirname(__file__), "logsupport", "log.ini") self.assertEqual(environ[evn], lc) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_runner.py0000644000076600000240000002330315011314374024375 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2015 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Unit tests for the testrunner's runner logic """ import sys import unittest from zope.testrunner import runner from zope.testrunner.layer import UnitTests class TestLayerOrdering(unittest.TestCase): def sort_key(self, layer): return ', '.join(qn.rpartition('.')[-1] for qn in runner.layer_sort_key(layer)) def order(self, *layers): return ', '.join(layer.__name__ for layer in runner.order_by_bases(layers)) def test_order_by_bases(self): # A B # /|\ / # / | \ / # A1 A2 AB class A: pass class A1(A): pass class A2(A): pass class B: pass class AB(A, B): pass self.assertEqual(self.order(B, A1, A2, A1, AB, UnitTests), 'UnitTests, A1, A2, B, AB') self.assertEqual(self.sort_key(UnitTests), '') self.assertEqual(self.sort_key(A1), 'A, A1') self.assertEqual(self.sort_key(A2), 'A, A2') self.assertEqual(self.sort_key(B), 'B') self.assertEqual(self.sort_key(AB), 'B, A, AB') def test_order_by_bases_alphabetical_order(self): class X: pass class Y: pass class Z: pass class A(Y): pass class B(X): pass class C(Z): pass # It'd be nice to get A, B, C here, but that's not trivial. self.assertEqual(self.order(A, B, C), 'B, A, C') self.assertEqual(self.sort_key(B), 'X, B') self.assertEqual(self.sort_key(A), 'Y, A') self.assertEqual(self.sort_key(C), 'Z, C') def test_order_by_bases_diamond_hierarchy(self): # A # / \ # B D # | | # C E # \ / # F class A: pass class B(A): pass class C(B): pass class D(A): pass class E(D): pass class F(C, E): pass self.assertEqual(self.order(A, B, C, D, E, F), 'A, B, C, D, E, F') self.assertEqual(self.sort_key(A), 'A') self.assertEqual(self.sort_key(B), 'A, B') self.assertEqual(self.sort_key(C), 'A, B, C') self.assertEqual(self.sort_key(D), 'A, D') self.assertEqual(self.sort_key(E), 'A, D, E') self.assertEqual(self.sort_key(F), 'A, D, E, B, C, F') # only those layers that were passed in are returned self.assertEqual(self.order(B, E), 'B, E') def test_order_by_bases_shared_setup_trumps_alphabetical_order(self): # A # / \ # AB AC # / \ \ # AAAABD \ MMMACF # ZZZABE class A: pass class AB(A): pass class AC(A): pass class AAAABD(AB): pass class ZZZABE(AB): pass class MMMACF(AC): pass self.assertEqual(self.order(AAAABD, MMMACF, ZZZABE), 'AAAABD, ZZZABE, MMMACF') self.assertEqual(self.sort_key(AAAABD), 'A, AB, AAAABD') self.assertEqual(self.sort_key(ZZZABE), 'A, AB, ZZZABE') self.assertEqual(self.sort_key(MMMACF), 'A, AC, MMMACF') def test_order_by_bases_multiple_inheritance(self): # Layerx Layer1 # | \ / \ # | \ Layer11 Layer12 # | \ | | / \ # | Layer111 | Layer121 Layer122 # \ / # \ / # Layer112 # class Layer1: pass class Layerx: pass class Layer11(Layer1): pass class Layer12(Layer1): pass class Layer111(Layerx, Layer11): pass class Layer121(Layer12): pass class Layer112(Layerx, Layer11): pass class Layer122(Layer12): pass self.assertEqual( self.order(Layer1, Layer11, Layer12, Layer111, Layer112, Layer121, Layer122), 'Layer1, Layer11, Layer111, Layer112, Layer12, Layer121, Layer122') self.assertEqual(self.order(Layer111, Layer12), 'Layer111, Layer12') self.assertEqual(self.order(Layerx, Layer1, Layer11, Layer112), 'Layer1, Layer11, Layerx, Layer112') self.assertEqual(self.sort_key(Layer111), 'Layer1, Layer11, Layerx, Layer111') self.assertEqual(self.sort_key(Layer12), 'Layer1, Layer12') def test_order_by_bases_reverse_tree(self): # E D F # \ / \ / # B C # \ / # A class F: pass class E: pass class D: pass class C(D, F): pass class B(D, E): pass class A(B, C): pass self.assertEqual(self.order(A, B, C), 'B, C, A') self.assertEqual(self.order(A, B, C, D, E, F), 'D, E, B, F, C, A') def test_order_by_bases_mro_is_complicated(self): # A C B E D # / \ | / \ | / \ # \ K1 K2 / # `---. _--' # \ \ / | # \ K3 / # \ |/ # ZZ class A: pass class B: pass class C: pass class D: pass class E: pass class K1(A, B, C): pass class K2(D, B, E): pass class K3(D, A): pass class ZZ(K1, K2, K3): pass self.assertEqual(self.order(K1, K2, K3, ZZ), 'K3, K2, K1, ZZ') # Sorting by reverse MRO, as computed by Python's MRO algorithm, # would put the layers in a different order: K3, K1, K2, ZZ. # Does that matter? The class diagram is symmetric, so I think not. def test_FakeInputContinueGenerator_close(self): # multiprocessing (and likely other forkful frameworks) want to # close sys.stdin. The test runner replaces sys.stdin with a # FakeInputContinueGenerator for some reason. It should be # closeable. f = runner.FakeInputContinueGenerator() f.close() @unittest.skipIf(sys.warnoptions, "Only done if no user override") class TestWarnings(unittest.TestCase): def test_warning_filter_default(self): # When we run tests, we run them with a 'default' simplefilter. # Note that this test will fail if PYTHONWARNINGS is set, # or a -W option was given, so we skip it import warnings # Save the current filters, ignoring the compiled regexes, # which can't be compared. old_filters = [(f[0], f[2], 4) for f in warnings.filters] with warnings.catch_warnings(): # Set up just like the runner does warnings.simplefilter('default') warnings.filterwarnings('module', category=DeprecationWarning, message=r'Please use assert\w+ instead.') new_filters = [(f[0], f[2], 4) for f in warnings.filters] # For some reason, catch_warnings doesn't fully reset things, # and we wind up with some duplicate entries in new_filters self.assertEqual(set(old_filters), set(new_filters)) def test_warnings_are_shown(self): import logging import warnings from zope.testing.loggingsupport import InstalledHandler handler = InstalledHandler("py.warnings", level=logging.WARNING) self.addCleanup(handler.uninstall) logging.captureWarnings(True) self.addCleanup(logging.captureWarnings, False) msg = "This should be visible by default" warnings.warn(msg, DeprecationWarning) self.assertEqual(1, len(handler.records)) self.assertIn('DeprecationWarning', handler.records[0].getMessage()) self.assertIn(msg, handler.records[0].getMessage()) class TestReprLines(unittest.TestCase): def test_unprintable(self): class C: def __repr__(self): raise Exception rl = runner.repr_lines(C(), max_width=10000) self.assertEqual(len(rl), 1) self.assertIn(".C'> instance at 0x", rl[0]) def test_simple(self): rl = runner.repr_lines("") self.assertEqual(rl, [repr("")]) def test_multiline(self): li = ["*" * 20 + " %d " % i + "*" * 20 for i in range(2)] rl = runner.repr_lines(li) self.assertEqual(rl, [ "[" + repr(li[0]) + ",", " " + repr(li[1]) + "]"]) def test_multiline_truncated(self): li = ["*" * 20 + "%d" % i + "*" * 20 for i in range(2)] rl = runner.repr_lines(li, max_lines=1) self.assertEqual(rl, [ "[" + repr(li[0]) + ",", "..."]) def test_line_truncated(self): rl = runner.repr_lines("*" * 20, max_width=10) self.assertEqual(rl, ["'******..."]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_subunit.py0000644000076600000240000000531315011314374024556 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2010 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Unit tests for the testrunner's subunit integration.""" import io import sys import unittest from zope.testrunner import formatter try: import subunit subunit except ImportError: def test_suite(): return unittest.TestSuite() else: class TestSubunitTracebackPrintingMixin: def makeByteStringFailure(self, text, encoding): try: # It's more accurate to just use the text directly. self.fail(text) except self.failureException: return sys.exc_info() def test_print_failure_containing_utf8_bytestrings(self): exc_info = self.makeByteStringFailure(chr(6514), 'utf8') self.subunit_formatter.test_failure(self, 0, exc_info) assert b"AssertionError: \xe1\xa5\xb2" in self.output.getvalue() # '\xe1\xa5\xb2'.decode('utf-8') == chr(6514) def test_print_error_containing_utf8_bytestrings(self): exc_info = self.makeByteStringFailure(chr(6514), 'utf8') self.subunit_formatter.test_error(self, 0, exc_info) assert b"AssertionError: \xe1\xa5\xb2" in self.output.getvalue() # '\xe1\xa5\xb2'.decode('utf-8') == chr(6514) class TestSubunitTracebackPrinting( TestSubunitTracebackPrintingMixin, unittest.TestCase): def setUp(self): class FormatterOptions: verbose = False options = FormatterOptions() self.output = io.BytesIO() self.subunit_formatter = formatter.SubunitOutputFormatter( options, stream=self.output) class TestSubunitV2TracebackPrinting( TestSubunitTracebackPrintingMixin, unittest.TestCase): def setUp(self): class FormatterOptions: verbose = False options = FormatterOptions() self.output = io.BytesIO() self.subunit_formatter = formatter.SubunitV2OutputFormatter( options, stream=self.output) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_threadsupport.py0000644000076600000240000000706215011314374025774 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2022 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## from _thread import start_new_thread from threading import Lock from threading import Thread from time import sleep from unittest import TestCase from unittest import skipUnless from ..threadsupport import current_frames from ..threadsupport import enumerate class ThreadMixin: """test thread.""" def __init__(self): self.lock = Lock() self.stopped = False def run(self): with self.lock: pass self.stopped = True class ThrThread(ThreadMixin, Thread): """``threading.Thread`` thread.""" def __init__(self, name): Thread.__init__(self, name=name) ThreadMixin.__init__(self) class DummyThread(ThreadMixin): def start(self): start_new_thread(self.run, ()) class Tests(TestCase): def setUp(self): self._threads = [] self._prethreads = enumerate() def tearDown(self): for t in self._threads: if not t.stopped: t.lock.release() sleep(0.01) def _mk_thread(self, name=None): """create a new thread and start it. Use a full thread if *name*; otherwise a dummy thread. """ t = ThrThread(name) if name else DummyThread() t.lock.acquire() t.start() self._threads.append(t) sleep(0.01) return t def alive(self): """alive threads not in ``_prethreads``.""" return [p for p in enumerate() if p.is_alive() and p not in self._prethreads] def test_thr_proxy(self): self.check_proxy("Test ThrThread") @skipUnless(current_frames, "no `current_frames`") def test_dummy_proxy(self): self.check_proxy() def check_proxy(self, name=None): t = self._mk_thread(name) prs = self.alive() self.assertEqual(len(prs), 1) pr = prs[0] self.assertTrue(pr.is_alive()) if name is None: self.assertTrue(pr.name.startswith("Dummy-")) self.assertIn("DummyThread", repr(pr)) else: self.assertEqual(pr.name, t.name) self.assertEqual(repr(pr), repr(t)) def test_thr_new_thread(self): self.check_new_thread("Test ThrThread") @skipUnless(current_frames, "no `current_frames`") def test_dummy_new_thread(self): self.check_new_thread() def check_new_thread(self, name=None): self._mk_thread(name and (name + "-1")) threads = self.alive() self._mk_thread(name and (name + "-2")) new_threads = [p for p in self.alive() if p not in threads] self.assertEqual(len(new_threads), 1) def test_thr_stopped(self): self.check_stopped("Test ThrThread") @skipUnless(current_frames, "no `current_frames`") def test_dummy_stopped(self): self.check_stopped() def check_stopped(self, name=None): t = self._mk_thread(name) t.lock.release() sleep(0.01) self.assertEqual(self.alive(), []) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/test_xml_output.py0000644000076600000240000001326215011314374025307 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2023 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Unit tests for the XML reports """ import contextlib import io import shutil import sys import tempfile import unittest from pathlib import Path from zope import testrunner class Base(unittest.TestCase): def tearDown(self): self._cleanup(self.tmpdir) return super().tearDown() def _cleanup(self, path): assert path.exists() shutil.rmtree(path) class TestXMLOutput(Base): """Test the XML output of `zope.testrunner` By default, `zope.testrunner` reports the results to the terminal. CI systems however, need a machine readable format to actually parse the results and display them. The `JUnit` package from the Java community, long ago, created an XML report format that since then every CI system is able to read. The `--xml` option tells the test runner to produce such a report. """ def setUp(self): self.tmpdir = Path(tempfile.mkdtemp()) directory_with_tests = Path(Path(__file__).parent, 'testrunner-ex') self.arg_defaults = [ '--path', str(directory_with_tests), '--tests-pattern', '^sampletestsf?$', ] self.default_argv = f'test -u --xml={self.tmpdir} -t sample3'.split() self.reports_folder = self.tmpdir / 'testreports' def _run_tests(self): stream1 = io.StringIO() stream2 = io.StringIO() with contextlib.redirect_stdout(stream1): with contextlib.redirect_stderr(stream2): testrunner.run_internal(self.arg_defaults, self.default_argv) def test_xml_report(self): self._run_tests() # A report has been created: self.assertTrue(self.reports_folder.exists()) # There are 5 reports: reports = [x for x in self.reports_folder.iterdir()] self.assertEqual(len(reports), 5) def test_xml_report_details(self): """Let's look at an individual report.""" self._run_tests() report = self.reports_folder / 'sample3.sampletests.TestA.xml' # 3 tests were run with no errors nor failures: content = report.read_text() self.assertIn(' tests="3" ', content) self.assertIn(' errors="0" ', content) self.assertIn(' failures="0" ', content) # The specific tests that were run: self.assertIn( '>> import os.path >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> testrunner.run_internal(defaults, 'test --layer 111'.split()) Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer111 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer111 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. False If options already have default values, then passing a different default will override. For example, --list-tests defaults to being turned off, but if we pass in a different default, that one takes effect. >>> defaults = [ ... '--list-tests', ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> testrunner.run_internal(defaults, 'test --layer 111'.split()) Listing samplelayers.Layer111 tests: test_x1 (sample1.sampletests.test111.TestA...) test_y0 (sample1.sampletests.test111.TestA...) test_z0 (sample1.sampletests.test111.TestA...) test_x0 (sample1.sampletests.test111.TestB...) test_y1 (sample1.sampletests.test111.TestB...) test_z0 (sample1.sampletests.test111.TestB...) test_1 (sample1.sampletests.test111.TestNotMuch...) test_2 (sample1.sampletests.test111.TestNotMuch...) test_3 (sample1.sampletests.test111.TestNotMuch...) test_x0 (sample1.sampletests.test111) test_y0 (sample1.sampletests.test111) test_z1 (sample1.sampletests.test111) /home/benji/workspace/zope.testrunner/1/src/zope/testing/testrunner/testrunner-ex/sample1/sampletests/../../sampletestsl.rst test_x1 (sampletests.test111.TestA...) test_y0 (sampletests.test111.TestA...) test_z0 (sampletests.test111.TestA...) test_x0 (sampletests.test111.TestB...) test_y1 (sampletests.test111.TestB...) test_z0 (sampletests.test111.TestB...) test_1 (sampletests.test111.TestNotMuch...) test_2 (sampletests.test111.TestNotMuch...) test_3 (sampletests.test111.TestNotMuch...) test_x0 (sampletests.test111) test_y0 (sampletests.test111) test_z1 (sampletests.test111) /home/benji/workspace/zope.testrunner/1/src/zope/testing/testrunner/testrunner-ex/sampletests/../sampletestsl.rst False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-colors.rst0000644000076600000240000004447715011314374025734 0ustar00m.howitzstaff================= Colorful output ================= If you're on a Unix-like system, you can ask for colorized output. The test runner emits terminal control sequences to highlight important pieces of information (such as the names of failing tests) in different colors. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner Since it wouldn't be a good idea to have terminal control characters in a test file, let's wrap sys.stdout in a simple terminal interpreter >>> import re >>> class Terminal(object): ... _color_regexp = re.compile('\033\\[([0-9;]*)m') ... _colors = {'0': 'normal', '1': 'bold', '30': 'black', '31': 'red', ... '32': 'green', '33': 'yellow', '34': 'blue', ... '35': 'magenta', '36': 'cyan', '37': 'grey'} ... def __init__(self, stream): ... self._stream = stream ... def __getattr__(self, attr): ... return getattr(self._stream, attr) ... def isatty(self): ... return True ... def write(self, text): ... if '\033[' in text: ... text = self._color_regexp.sub(self._color, text) ... self._stream.write(text) ... def writelines(self, lines): ... for line in lines: ... self.write(line) ... def _color(self, match): ... colorstring = '{' ... for number in match.group(1).split(';'): ... colorstring += self._colors.get(number, '?') ... return colorstring + '}' >>> real_stdout = sys.stdout >>> sys.stdout = Terminal(sys.stdout) Successful test =============== A successful test run soothes the developer with warm green colors: >>> sys.argv = 'test --layer 122 -c'.split() >>> testrunner.run_internal(defaults) {normal}Running samplelayers.Layer122 tests:{normal} Set up samplelayers.Layer1 in {green}0.000{normal} seconds. Set up samplelayers.Layer12 in {green}0.000{normal} seconds. Set up samplelayers.Layer122 in {green}0.000{normal} seconds. {normal} Ran {green}26{normal} tests with {green}0{normal} failures, {green}0{normal} errors, {green}0{normal} skipped in {green}0.007{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down samplelayers.Layer122 in {green}0.000{normal} seconds. Tear down samplelayers.Layer12 in {green}0.000{normal} seconds. Tear down samplelayers.Layer1 in {green}0.000{normal} seconds. False Failed test =========== A failed test run highlights the failures in red: >>> sys.argv = 'test -c --tests-pattern ^sampletests(f|_e|_f)?$ '.split() >>> testrunner.run_internal(defaults) {normal}Running zope.testrunner.layer.UnitTests tests:{normal} Set up zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. {boldred}Failure in test eek (sample2.sampletests_e){normal} Failed doctest test for sample2.sampletests_e.eek File "testrunner-ex/sample2/sampletests_e.py", line 28, in eek ... {normal}File "{boldblue}testrunner-ex/sample2/sampletests_e.py{normal}", line {boldred}30{normal}, in {boldcyan}sample2.sampletests_e.eek{normal} Failed example: {cyan} f(){normal} Exception raised: {red} Traceback (most recent call last):{normal} {red} File ".../doctest.py", line 1356, in __run{normal} {red} ...{normal} {red} File "", line 1, in ?{normal} {red} f(){normal} {red} File "testrunner-ex/sample2/sampletests_e.py", line 19, in f{normal} {red} g(){normal} {red} File "testrunner-ex/sample2/sampletests_e.py", line 24, in g{normal} {red} x = y + 1 # noqa: F821{normal} {red} - __traceback_info__: I don't know what Y should be.{normal} {red} NameError: name 'y' is not defined{normal} {boldred}Error in test test3 (sample2.sampletests_e.Test...){normal} Traceback (most recent call last): {normal} File "{boldblue}unittest.py{normal}", line {boldred}260{normal}, in {boldcyan}run{normal} {cyan} testMethod(){normal} {normal} File "{boldblue}testrunner-ex/sample2/sampletests_e.py{normal}", line {boldred}43{normal}, in {boldcyan}test3{normal} {cyan} f(){normal} {normal} File "{boldblue}testrunner-ex/sample2/sampletests_e.py{normal}", line {boldred}19{normal}, in {boldcyan}f{normal} {cyan} g(){normal} {normal} File "{boldblue}testrunner-ex/sample2/sampletests_e.py{normal}", line {boldred}24{normal}, in {boldcyan}g{normal} {cyan} x = y + 1 # noqa: F821{normal} {red} - __traceback_info__: I don't know what Y should be.{normal} {red}NameError: name 'y' is not defined{normal} {boldred}Failure in test testrunner-ex/sample2/e.rst{normal} Failed doctest test for e.rst File "testrunner-ex/sample2/e.rst", line 0 ... {normal}File "{boldblue}testrunner-ex/sample2/e.rst{normal}", line {boldred}4{normal}, in {boldcyan}e.rst{normal} Failed example: {cyan} f(){normal} Exception raised: {red} Traceback (most recent call last):{normal} {red} File ".../doctest.py", line 1356, in __run{normal} {red} ...{normal} {red} File "", line 1, in ?{normal} {red} f(){normal} {red} File "", line 2, in f{normal} {red} return x{normal} {red} NameError: name 'x' is not defined{normal} {boldred}Failure in test test (sample2.sampletests_f.Test...){normal} Traceback (most recent call last): {normal} File "{boldblue}unittest.py{normal}", line {boldred}260{normal}, in {boldcyan}run{normal} {cyan} testMethod(){normal} {normal} File "{boldblue}testrunner-ex/sample2/sampletests_f.py{normal}", line {boldred}21{normal}, in {boldcyan}test{normal} {cyan} self.assertEqual(1, 0){normal} {normal} File "{boldblue}unittest.py{normal}", line {boldred}333{normal}, in {boldcyan}failUnlessEqual{normal} {cyan} raise self.failureException, \{normal} {red}AssertionError: 1 != 0{normal} {normal} Ran {green}164{normal} tests with {boldred}3{normal} failures, {boldred}1{normal} errors, {green}0{normal} skipped in {green}0.045{normal} seconds.{normal} ... {normal}Total: {green}329{normal} tests, {boldred}3{normal} failures, {boldred}1{normal} errors, {green}0{normal} skipped in {green}N.NNN{normal} seconds.{normal} True Skipped tests ============= Tests which are skipped are colorized:: >>> defaults_skip = [ ... '--path', os.path.join(this_directory, 'testrunner-ex-skip'), ... '--tests-pattern', '^sample_skipped_tests$', ... ] >>> sys.argv = 'test --tests-pattern ^sample_skipped_tests$ -t NoLayer -c -vvvvvv'.split() >>> _ = testrunner.run_internal(defaults_skip) {normal}Running tests at level 1{normal} {normal}Running zope.testrunner.layer.UnitTests tests:{normal} Set up zope.testrunner.layer.UnitTests in {green}0.000{normal} seconds. {normal} Running:{normal} test_skipped (sample_skipped_tests.TestSkipppedNoLayer...) ({boldyellow}skipped: I'm a skipped test!{normal}) {normal} Ran {green}1{normal} tests with {green}0{normal} failures, {green}0{normal} errors, {boldyellow}1{normal} skipped in {green}N.NNN{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. Doctest failures ================ The expected and actual outputs of failed doctests are shown in different colors: >>> sys.argv = 'test --tests-pattern ^pledge$ -c'.split() >>> _ = testrunner.run_internal(defaults) {normal}Running zope.testrunner.layer.UnitTests tests:{normal} Set up zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. {boldred}Failure in test pledge (pledge){normal} Failed doctest test for pledge.pledge File "testrunner-ex/pledge.py", line 24, in pledge ... {normal}File testrunner-ex/pledge.py{normal}", line {boldred}26{normal}, in {boldcyan}pledge.pledge{normal} Failed example: {cyan} print_pledge(){normal} Expected: {green} I give my pledge, as an earthling,{normal} {green} to save, and faithfully, to defend from waste,{normal} {green} the natural resources of my planet.{normal} {green} It's soils, minerals, forests, waters, and wildlife.{normal} {green} {normal} Got: {red} I give my pledge, as and earthling,{normal} {red} to save, and faithfully, to defend from waste,{normal} {red} the natural resources of my planet.{normal} {red} It's soils, minerals, forests, waters, and wildlife.{normal} {red} {normal} {normal} Ran {green}1{normal} tests with {boldred}1{normal} failures, {green}0{normal} errors, {green}0{normal} skipped in {green}0.002{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. Diffs are highlighted so you can easily tell the context and the mismatches apart: >>> sys.argv = 'test --tests-pattern ^pledge$ --ndiff -c'.split() >>> _ = testrunner.run_internal(defaults) {normal}Running zope.testrunner.layer.UnitTests tests:{normal} Set up zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. {boldred}Failure in test pledge (pledge){normal} Failed doctest test for pledge.pledge File "testrunner-ex/pledge.py", line 24, in pledge ... {normal}File testrunner-ex/pledge.py{normal}", line {boldred}26{normal}, in {boldcyan}pledge.pledge{normal} Failed example: {cyan} print_pledge(){normal} Differences (ndiff with -expected +actual): {green} - I give my pledge, as an earthling,{normal} {red} + I give my pledge, as and earthling,{normal} {magenta} ? +{normal} {normal} to save, and faithfully, to defend from waste,{normal} {normal} the natural resources of my planet.{normal} {normal} It's soils, minerals, forests, waters, and wildlife.{normal} {normal} {normal} {normal} Ran {green}1{normal} tests with {boldred}1{normal} failures, {green}0{normal} errors, {green}0{normal} skipped in {green}0.003{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. Even test failures that have actual blank lines (as opposed to ) in them are highlighted correctly. >>> import zope.testrunner.formatter >>> formatter = zope.testrunner.formatter.ColorfulOutputFormatter(None) >>> formatter.print_doctest_failure("""\ ... File "sometest.rst", line 221, in sometest.rst ... Failed example: ... foo() ... Expected: ... Output that contains ... ... blank lines. ... Got: ... Output that still contains ... ... blank lines.""") {normal} File "sometest.rst", line 221, in sometest.rst{normal} Failed example: {cyan} foo(){normal} Expected: {green} Output that contains{normal} {green} blank lines.{normal} Got: {red} Output that still contains{normal} {red} blank lines.{normal} Timing individual tests ======================= At very high verbosity levels you can see the time taken by each test >>> sys.argv = 'test -u -t test_one.TestNotMuch -c -vvv'.split() >>> testrunner.run_internal(defaults) {normal}Running tests at level 1{normal} {normal}Running zope.testrunner.layer.UnitTests tests:{normal} Set up zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. {normal} Running:{normal} test_1 (sample1.sampletests.test_one.TestNotMuch...) ({green}N.NNN s{normal}) test_2 (sample1.sampletests.test_one.TestNotMuch...) ({green}N.NNN s{normal}) test_3 (sample1.sampletests.test_one.TestNotMuch...) ({green}N.NNN s{normal}) test_1 (sampletests.test_one.TestNotMuch...) ({green}N.NNN s{normal}) test_2 (sampletests.test_one.TestNotMuch...) ({green}N.NNN s{normal}) test_3 (sampletests.test_one.TestNotMuch...) ({green}N.NNN s{normal}) {normal} Ran {green}6{normal} tests with {green}0{normal} failures, {green}0{normal} errors, {green}0{normal} skipped in {green}N.NNN{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. False If we had very slow tests we would see their times highlighted in a different color. Instead of creating a test that waits 10 seconds, let's lower the slow test threshold in the test runner to 0 seconds to make all of the tests seem slow. >>> sys.argv = 'test -u -t test_one.TestNotMuch -c -vvv --slow-test 0'.split() >>> testrunner.run_internal(defaults) {normal}Running tests at level 1{normal} {normal}Running zope.testrunner.layer.UnitTests tests:{normal} Set up zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. {normal} Running:{normal} test_1 (sample1.sampletests.test_one.TestNotMuch...) ({boldmagenta}N.NNN s{normal}) test_2 (sample1.sampletests.test_one.TestNotMuch...) ({boldmagenta}N.NNN s{normal}) test_3 (sample1.sampletests.test_one.TestNotMuch...) ({boldmagenta}N.NNN s{normal}) test_1 (sampletests.test_one.TestNotMuch...) ({boldmagenta}N.NNN s{normal}) test_2 (sampletests.test_one.TestNotMuch...) ({boldmagenta}N.NNN s{normal}) test_3 (sampletests.test_one.TestNotMuch...) ({boldmagenta}N.NNN s{normal}) {normal} Ran {green}6{normal} tests with {green}0{normal} failures, {green}0{normal} errors, {green}0{normal} skipped in {green}N.NNN{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down zope.testrunner.layer.UnitTests in {green}N.NNN{normal} seconds. False Disabling colors ================ If -c or --color have been previously provided on the command line (perhaps by a test runner wrapper script), but no colorized output is desired, the -C or --no-color options will disable colorized output: >>> sys.argv = 'test --layer 122 -c -C'.split() >>> testrunner.run_internal(defaults) Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.007 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False >>> sys.argv = 'test --layer 122 -c --no-color'.split() >>> testrunner.run_internal(defaults) Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.007 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False Autodetecting colors ==================== The --auto-color option will determine if stdout is a terminal that supports colors, and only enable colorized output if so. Our ``Terminal`` wrapper pretends it is a terminal, but the curses module will realize it isn't: >>> sys.argv = 'test --layer 122 --auto-color'.split() >>> testrunner.run_internal(defaults) Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.007 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False We can fake it >>> class FakeCurses(object): ... class error(Exception): ... pass ... def setupterm(self): ... pass ... def tigetnum(self, attr): ... return dict(colors=8).get(attr, -2) >>> sys.modules['curses'] = FakeCurses() >>> sys.argv = 'test --layer 122 --auto-color'.split() >>> testrunner.run_internal(defaults) {normal}Running samplelayers.Layer122 tests:{normal} Set up samplelayers.Layer1 in {green}0.000{normal} seconds. Set up samplelayers.Layer12 in {green}0.000{normal} seconds. Set up samplelayers.Layer122 in {green}0.000{normal} seconds. {normal} Ran {green}26{normal} tests with {green}0{normal} failures, {green}0{normal} errors, {green}0{normal} skipped in {green}0.007{normal} seconds.{normal} {normal}Tearing down left over layers:{normal} Tear down samplelayers.Layer122 in {green}0.000{normal} seconds. Tear down samplelayers.Layer12 in {green}0.000{normal} seconds. Tear down samplelayers.Layer1 in {green}0.000{normal} seconds. False >>> del sys.modules['curses'] The real stdout is not a terminal in a doctest: >>> sys.stdout = real_stdout >>> sys.argv = 'test --layer 122 --auto-color'.split() >>> testrunner.run_internal(defaults) Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.007 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-coverage-win32.rst0000644000076600000240000000171715011314374027154 0ustar00m.howitzstaffCode Coverage ============= On Windows drive names can be upper and lower case, these can be randomly passed to TestIgnore.names. Watch out for the case of the R drive! >>> from zope.testrunner.coverage import TestIgnore >>> ignore = TestIgnore((('r:\\winproject\\src\\blah\\foo', ''), ... ('R:\\winproject\\src\\blah\\bar', ''))) >>> ignore._test_dirs ['r:\\winproject\\src\\blah\\foo\\', 'R:\\winproject\\src\\blah\\bar\\'] We can now ask whether a particular module should be ignored: >>> ignore.names('r:\\winproject\\src\\blah\\foo\\baz.py', 'baz') False >>> ignore.names('R:\\winproject\\src\\blah\\foo\\baz.py', 'baz') False >>> ignore.names('r:\\winproject\\src\\blah\\bar\\zab.py', 'zab') False >>> ignore.names('R:\\winproject\\src\\blah\\bar\\zab.py', 'zab') False >>> ignore.names('r:\\winproject\\src\\blah\\hello.py', 'hello') True >>> ignore.names('R:\\winproject\\src\\blah\\hello.py', 'hello') True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-coverage.rst0000644000076600000240000001375115011314374026215 0ustar00m.howitzstaff=============== Code Coverage =============== .. note:: The coverage reports produced here are those generated by the standard `trace` module. They are not compatible with those produced by `coverage `_. .. note:: You can run zope.testrunner with the common `coverage `_ tool to take advantage of its speed and integration with other tools: coverage run -m zope.testrunner --test-path src ...other options... If the ``--coverage`` option is used, test coverage reports will be generated. The directory name given as the parameter will be used to hold the reports. >>> import os.path, sys, tempfile >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> tempdir = tempfile.mkdtemp(prefix='zope.testrunner-') >>> coverage_dir = os.path.join(tempdir, 'coverage_dir') >>> sys.argv = ['test', '--coverage', coverage_dir] >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in 0.000 seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in 0.687 seconds. Running samplelayers.Layer1 tests: Tear down zope.testrunner.layer.UnitTests in 0.000 seconds. Set up samplelayers.Layer1 in 0.000 seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Running samplelayers.Layer11 tests: Set up samplelayers.Layer11 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.125 seconds. Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in 0.000 seconds. Set up samplelayers.Layer111 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.125 seconds. Running samplelayers.Layer112 tests: Tear down samplelayers.Layer111 in 0.000 seconds. Set up samplelayers.Layer112 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.125 seconds. Running samplelayers.Layer12 tests: Tear down samplelayers.Layer112 in 0.000 seconds. Tear down samplelayers.Layerx in 0.000 seconds. Tear down samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.140 seconds. Running samplelayers.Layer121 tests: Set up samplelayers.Layer121 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.125 seconds. Running samplelayers.Layer122 tests: Tear down samplelayers.Layer121 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.125 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. lines cov% module (path) ... 48 100% sampletests.test1 (testrunner-ex/sampletests/test1.py) 74 100% sampletests.test11 (testrunner-ex/sampletests/test11.py) 74 100% sampletests.test111 (testrunner-ex/sampletests/test111.py) 76 100% sampletests.test112 (testrunner-ex/sampletests/test112.py) 74 100% sampletests.test12 (testrunner-ex/sampletests/test12.py) 74 100% sampletests.test121 (testrunner-ex/sampletests/test121.py) 74 100% sampletests.test122 (testrunner-ex/sampletests/test122.py) 48 100% sampletests.test_one (testrunner-ex/sampletests/test_one.py) 112 95% sampletestsf (testrunner-ex/sampletestsf.py) Total: 321 tests, 0 failures, 0 errors and 0 skipped in 0.630 seconds. False The directory specified with the ``--coverage`` option will have been created and will hold the coverage reports. >>> os.path.exists(coverage_dir) True >>> os.listdir(coverage_dir) [...] (We should clean up after ourselves.) >>> import shutil >>> shutil.rmtree(tempdir) Ignoring Tests ============== The `trace` module supports ignoring directories and modules based the test selection. Only directories selected for testing should report coverage. The test runner provides a custom implementation of the relevant API. The ``TestIgnore`` class, the class managing the ignoring, is initialized by passing the command line options. It uses the options to determine the directories that should be covered. >>> class FauxOptions(object): ... package = None ... test_path = [('/myproject/src/blah/foo', ''), ... ('/myproject/src/blah/bar', '')] >>> from zope.testrunner import coverage >>> from zope.testrunner.find import test_dirs >>> ignore = coverage.TestIgnore(test_dirs(FauxOptions(), {})) >>> ignore._test_dirs ['/myproject/src/blah/foo/', '/myproject/src/blah/bar/'] We can now ask whether a particular module should be ignored: >>> ignore.names('/myproject/src/blah/foo/baz.py', 'baz') False >>> ignore.names('/myproject/src/blah/bar/mine.py', 'mine') False >>> ignore.names('/myproject/src/blah/foo/__init__.py', 'foo') False >>> ignore.names('/myproject/src/blah/hello.py', 'hello') True When running the test runner, modules are sometimes created from text strings. Those should *always* be ignored: >>> ignore.names('/myproject/src/blah/hello.rst', '') True To make this check fast, the class implements a cache. In an early implementation, the result was cached by the module name, which was a problem, since a lot of modules carry the same name (not the Python dotted name here!). So just because a module has the same name in an ignored and tested directory, does not mean it is always ignored: >>> ignore.names('/myproject/src/blah/module.py', 'module') True >>> ignore.names('/myproject/src/blah/foo/module.py', 'module') False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-debugging-import-failure.rst0000644000076600000240000000426215011314374031307 0ustar00m.howitzstaffRegression tests for https://bugs.launchpad.net/zope.testrunner/+bug/1119363 Post-mortem debugging also works when there is an import failure. >>> import os, shutil, sys, tempfile >>> tdir = tempfile.mkdtemp() >>> dir = os.path.join(tdir, 'TESTS-DIR') >>> os.mkdir(dir) >>> def write_file(filename, body): ... with open(os.path.join(dir, filename), 'w') as f: ... f.write(body) ... try: ... # Need to do this after creating new modules: ... import importlib; importlib.invalidate_caches() ... except (ImportError, AttributeError): ... pass >>> write_file('tests.py', ... ''' ... impot doctest ... ''') >>> class Input: ... def __init__(self, src): ... self.lines = src.split('\n') ... def readline(self): ... line = self.lines.pop(0) ... print(line) ... return line+'\n' >>> real_stdin = sys.stdin >>> sys.stdin = Input('c') >>> sys.argv = [testrunner_script] >>> import zope.testrunner >>> try: ... zope.testrunner.run_internal(['--path', dir, '-D']) ... except zope.testrunner.interfaces.EndRun: print('EndRun raised') ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS +REPORT_NDIFF File ".../TESTS-DIR/tests.py", line 2... impot doctest ...^ SyntaxError: invalid syntax... > ...find.py(399)import_name() -> __import__(name) (Pdb) c EndRun raised Post-mortem debugging also works when the test suite is invalid: >>> sys.stdin = Input('c') >>> write_file('tests2.py', ... ''' ... def test_suite(): ... return None ... ''') >>> import sys >>> try: ... zope.testrunner.run_internal( ... ['--path', dir, '-Dvv', '--tests-pattern', 'tests2']) ... except zope.testrunner.interfaces.EndRun: print('EndRun raised') ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS +REPORT_NDIFF TypeError: Invalid test_suite, None, in tests2 > ...find.py(243)find_suites() -> raise TypeError(bad_test_suite_msg) (Pdb) c EndRun raised >>> shutil.rmtree(tdir) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-debugging-layer-setup.rst0000644000076600000240000000753615011314374030631 0ustar00m.howitzstaffPost-mortem debugging also works when there is a failure in layer setup. >>> import os, shutil, sys, tempfile >>> tdir = tempfile.mkdtemp() >>> dir = os.path.join(tdir, 'TESTS-DIR') >>> os.mkdir(dir) >>> def write_file(filename, body): ... with open(os.path.join(dir, filename), 'w') as f: ... f.write(body) ... try: ... # Need to do this after creating new modules: ... import importlib; importlib.invalidate_caches() ... except (ImportError, AttributeError): ... pass >>> write_file('tests.py', ... ''' ... import doctest ... ... class Layer: ... @classmethod ... def setUp(self): ... x = 1 ... raise ValueError ... ... def a_test(): ... """ ... >>> None ... """ ... def test_suite(): ... suite = doctest.DocTestSuite() ... suite.layer = Layer ... return suite ... ... ''') >>> class Input: ... def __init__(self, src): ... self.lines = src.split('\n') ... def readline(self): ... line = self.lines.pop(0) ... print(line) ... return line+'\n' >>> real_stdin = sys.stdin >>> sys.stdin = Input('p x\nc') >>> sys.argv = [testrunner_script] >>> import zope.testrunner >>> try: ... zope.testrunner.run_internal(['--path', dir, '-D']) ... except zope.testrunner.interfaces.EndRun: print("unexpected EndRun!") ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS Running tests.Layer tests: Set up tests.Layer ...ValueError > ...tests.py(8)setUp() -> raise ValueError (Pdb) p x 1 (Pdb) c False Note that post-mortem debugging doesn't work when the layer is run in a subprocess: >>> sys.stdin = Input('p x\nc') >>> write_file('tests2.py', ... ''' ... import doctest, unittest ... ... class Layer1: ... @classmethod ... def setUp(self): ... pass ... ... @classmethod ... def tearDown(self): ... raise NotImplementedError ... ... class Layer2: ... @classmethod ... def setUp(self): ... x = 1 ... raise ValueError ... ... def a_test(): ... """ ... >>> None ... """ ... def test_suite(): ... suite1 = doctest.DocTestSuite() ... suite1.layer = Layer1 ... suite2 = doctest.DocTestSuite() ... suite2.layer = Layer2 ... return unittest.TestSuite((suite1, suite2)) ... ... ''') >>> import sys >>> try: ... zope.testrunner.run_internal( ... ['--path', dir, '-Dvv', '--tests-pattern', 'tests2']) ... except zope.testrunner.interfaces.EndRun: print("unexpected EndRun!") ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS +REPORT_NDIFF Running tests at level 1 Running tests2.Layer1 tests: Set up tests2.Layer1 in 0.000 seconds. Running: a_test (tests2) Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.001 seconds. Running tests2.Layer2 tests: Tear down tests2.Layer1 ... not supported Running in a subprocess. Set up tests2.Layer2 ********************************************************************** Can't post-mortem debug when running a layer as a subprocess! Try running layer 'tests2.Layer2' by itself. ********************************************************************** Traceback (most recent call last): ... raise ValueError ValueError Tests with errors: Layer: tests2.Layer2.setUp Total: 1 tests, 0 failures, 1 errors and 0 skipped in 0.210 seconds. True >>> shutil.rmtree(tdir) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-debugging-nonprintable-exc.rst0000644000076600000240000000321315011314374031613 0ustar00m.howitzstaffRegression tests for https://github.com/zopefoundation/zope.testrunner/issues/8 Post-mortem debugging also works when the exception cannot be printed >>> import os, shutil, sys, tempfile >>> tdir = tempfile.mkdtemp() >>> dir = os.path.join(tdir, 'TESTS-DIR') >>> os.mkdir(dir) >>> def write_file(filename, body): ... with open(os.path.join(dir, filename), 'w') as f: ... f.write(body) ... try: ... # Need to do this on after creating new modules: ... import importlib; importlib.invalidate_caches() ... except (ImportError, AttributeError): ... pass >>> write_file('tests.py', ... r''' ... import unittest ... ... class MyTest(unittest.TestCase): ... def test(self): ... self.assertEqual('a', b'\xc4\x85'.decode('UTF-8')) ... ''') >>> class Input: ... def __init__(self, src): ... self.lines = src.split('\n') ... def readline(self): ... line = self.lines.pop(0) ... print(line) ... return line+'\n' >>> real_stdin = sys.stdin >>> sys.stdin = Input('c') >>> sys.argv = [testrunner_script] >>> import zope.testrunner >>> try: ... zope.testrunner.run_internal(['--path', dir, '-D']) ... except zope.testrunner.interfaces.EndRun: print('EndRun raised') ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS +REPORT_NDIFF Running zope.testrunner.layer.UnitTests tests: ... AssertionError: ...'a' != ...'...' ... (Pdb) c Tearing down left over layers: ... False >>> shutil.rmtree(tdir) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-debugging.rst0000644000076600000240000001453015011314374026351 0ustar00m.howitzstaff========================= Debugging Test Failures ========================= The testrunner module supports post-mortem debugging and debugging using `pdb.set_trace`. Let's look first at using `pdb.set_trace`. To demonstrate this, we'll provide input via helper Input objects: >>> class Input: ... def __init__(self, src): ... self.lines = src.split('\n') ... def readline(self): ... line = self.lines.pop(0) ... print(line) ... return line+'\n' If a test or code called by a test calls pdb.set_trace, then the runner will enter pdb at that point: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> real_stdin = sys.stdin >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t set_trace1').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: ... > testrunner-ex/sample3/sampletests_d.py(27)test_set_trace1() -> ... (Pdb) p x 1 (Pdb) c Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.001 seconds. ... False Post-Mortem Debugging ===================== You can also do post-mortem debugging, using the --post-mortem (-D) option: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem1 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: ... Error in test test_post_mortem1 (sample3.sampletests_d.TestSomething...) Traceback (most recent call last): File "testrunner-ex/sample3/sampletests_d.py", line 34, in test_post_mortem1 raise ValueError ValueError ...ValueError > testrunner-ex/sample3/sampletests_d.py(34)test_post_mortem1() -> raise ValueError (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Note that the test runner exits after post-mortem debugging. In the example above, we debugged an error. Failures are actually converted to errors and can be debugged the same way: >>> sys.stdin = Input('p x\np y\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem_failure1 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: ... Error in test test_post_mortem_failure1 (sample3.sampletests_d.TestSomething...) Traceback (most recent call last): File ".../unittest.py", line 252, in debug getattr(self, self.__testMethodName)() File "testrunner-ex/sample3/sampletests_d.py", line 42, in test_post_mortem_failure1 assert x == y AssertionError ...AssertionError > testrunner-ex/sample3/sampletests_d.py(42)test_post_mortem_failure1() -> assert x == y (Pdb) p x 1 (Pdb) p y 2 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Skipping tests with ``@unittest.skip`` decorator does not trigger the post-mortem debugger: >>> sys.stdin = Input('q') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t skipped -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 1 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Tests marked as expected failures with the ``@unittest.expectedFailure`` decorator do not trigger the post-mortem debugger when they fail as expected: >>> expected_failure_tests_defaults = [ ... '--path', os.path.join(this_directory, 'testrunner-ex-expectedFailure'), ... '--tests-pattern', '^sample_expected_failure_tests$', ... ] >>> sys.stdin = Input('q') >>> sys.argv = 'test -t test_expected_failure -D'.split() >>> try: testrunner.run_internal(expected_failure_tests_defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False When ``@unittest.expectedFailure`` test unexpectedly pass, it's not possible to use the post-mortem debugger, because no exception was raised. In that case a warning is printed: >>> sys.stdin = Input('q') >>> sys.argv = 'test -t test_unexpected_success -D'.split() >>> try: testrunner.run_internal(expected_failure_tests_defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Error in test test_unexpected_success (sample_expected_failure_tests.TestExpectedFailures...) Traceback (most recent call last): zope.testrunner.runner.UnexpectedSuccess ********************************************************************** Can't post-mortem debug an unexpected success ********************************************************************** Ran 1 tests with 1 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-discovery.rst0000644000076600000240000000353615011314374026431 0ustar00m.howitzstaff================================= Automatically Discovering Tests ================================= You can explicitly specify which tests to run by providing a function that returns a `unittest.TestSuite` in the test modules (the name of the function can be configured with the ``--suite-name parameter``, it defaults to ``test_suite``). If no such function is present, testrunner will use all classes in the module that inherit from `unittest.TestCase` as tests: >>> import os, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = ['test', ... '--tests-pattern', '^sampletests_discover$', ... ] >>> testrunner.run(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False If the module neither provides a TestSuite nor has discoverable tests, testrunner will exit with an error to prevent acidentally missing test cases: >>> sys.argv = ['test', ... '--tests-pattern', '^sampletests_discover_notests$', ... ] >>> testrunner.run(defaults) Test-module import failures: Module: sample1.sampletests_discover_notests TypeError: Module sample1.sampletests_discover_notests does not define any tests Test-modules with import problems: sample1.sampletests_discover_notests Total: 0 tests, 0 failures, 0 errors and 0 skipped in 0.000 seconds. True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-edge-cases.rst0000644000076600000240000004373415011314374026426 0ustar00m.howitzstafftestrunner Edge Cases ===================== This document has some edge-case examples to test various aspects of the test runner. Separating Python path and test directories ------------------------------------------- The --path option defines a directory to be searched for tests *and* a directory to be added to Python's search path. The --test-path option can be used when you want to set a test search path without also affecting the Python path: >>> import os, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--test-path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = ['test'] >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS Test-module import failures: Module: sampletestsf Traceback (most recent call last): ModuleNotFoundError: No module named 'sampletestsf' ... >>> sys.path.append(directory_with_tests) >>> sys.argv = ['test'] >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer1 tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up samplelayers.Layer1 in 0.000 seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. ... Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 321 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False Bug #251759: The test runner's protection against descending into non-package directories was ineffective, e.g. picking up tests from eggs that were stored close by: >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex-251759') >>> defaults = [ ... '--test-path', directory_with_tests, ... ] >>> testrunner.run_internal(defaults) Total: 0 tests, 0 failures, 0 errors and 0 skipped in 0.000 seconds. False Debugging Edge Cases -------------------- >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--test-path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> class Input: ... def __init__(self, src): ... self.lines = src.split('\n') ... def readline(self): ... line = self.lines.pop(0) ... print(line) ... return line+'\n' >>> real_stdin = sys.stdin Using pdb.set_trace in a function called by an ordinary test: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t set_trace2').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests:... > testrunner-ex/sample3/sampletests_d.py(47)f() -> ... (Pdb) p x 1 (Pdb) c Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.001 seconds. ... False Using pdb.set_trace in a function called by a doctest in a doc string: >>> sys.stdin = Input('n\np x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t set_trace4').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. > testrunner-ex/sample3/sampletests_d.py(NNN)f() -> ... (Pdb) n ... (Pdb) p x 1 (Pdb) c Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Using pdb in a docstring-based doctest >>> sys.stdin = Input('n\np x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t set_trace3').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. ... (Pdb) n ... (Pdb) p x 1 (Pdb) c Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Using pdb.set_trace in a doc file: >>> sys.stdin = Input('n\np x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t set_trace5').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. ... (Pdb) n ... (Pdb) p x 1 (Pdb) c Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Using pdb.set_trace in a function called by a doctest in a doc file: >>> sys.stdin = Input('n\np x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t set_trace6').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. ... (Pdb) n ... (Pdb) p x 1 (Pdb) c Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging function called from ordinary test: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem2 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests:... Error in test test_post_mortem2 (sample3.sampletests_d.TestSomething...) Traceback (most recent call last): File "testrunner-ex/sample3/sampletests_d.py", line 37, in test_post_mortem2 g() File "testrunner-ex/sample3/sampletests_d.py", line 46, in g raise ValueError ValueError ...ValueError > testrunner-ex/sample3/sampletests_d.py(46)g() -> raise ValueError (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging docstring-based doctest: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem3 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Error in test post_mortem3 (sample3.sampletests_d) Traceback (most recent call last): ...UnexpectedException: testrunner-ex/sample3/sampletests_d.py:NNN (2 examples)> ...ValueError > (1)?() (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging function called from docstring-based doctest: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem4 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Error in test post_mortem4 (sample3.sampletests_d) Traceback (most recent call last): ...UnexpectedException: testrunner-ex/sample3/sampletests_d.py:NNN (1 example)> ...ValueError > testrunner-ex/sample3/sampletests_d.py(NNN)g() -> raise ValueError (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging file-based doctest: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem5 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Error testrunner-ex/sample3/post_mortem5.rst Traceback (most recent call last): ...UnexpectedException: testrunner-ex/sample3/post_mortem5.rst:0 (2 examples)> ...ValueError > (1)?() (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging function called from file-based doctest: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem6 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests:... Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Error testrunner-ex/sample3/post_mortem6.rst Traceback (most recent call last): File ".../zope/testing/doctest/__init__.py", Line NNN, in debug runner.run(self._dt_test, clear_globs=False) File ".../zope/testing/doctest/__init__.py", Line NNN, in run r = DocTestRunner.run(self, test, compileflags, out, False) File ".../zope/testing/doctest/__init__.py", Line NNN, in run return self.__run(test, compileflags, out) File ".../zope/testing/doctest/__init__.py", Line NNN, in __run exc_info) File ".../zope/testing/doctest/__init__.py", Line NNN, in report_unexpected_exception raise UnexpectedException(test, example, exc_info) ...UnexpectedException: testrunner-ex/sample3/post_mortem6.rst:0 (2 examples)> ...ValueError > testrunner-ex/sample3/sampletests_d.py(NNN)g() -> raise ValueError (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging of a docstring doctest failure: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem_failure2 -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests:... Error in test post_mortem_failure2 (sample3.sampletests_d) File "testrunner-ex/sample3/sampletests_d.py", line 81, in sample3.sampletests_d.post_mortem_failure2 x Want: 2 Got: 1 > testrunner-ex/sample3/sampletests_d.py(81)_() ...ValueError: Expected and actual output are different > (1)...() (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging of a docfile doctest failure: >>> sys.stdin = Input('p x\nc') >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$' ... ' -t post_mortem_failure.rst -D').split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin ... # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests:... Error in test /home/jim/z3/zope.testrunner/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.rst File "testrunner-ex/sample3/post_mortem_failure.rst", line 2, in post_mortem_failure.rst x Want: 2 Got: 1 > testrunner-ex/sample3/post_mortem_failure.rst(2)_() ...ValueError: Expected and actual output are different > (1)...() (Pdb) p x 1 (Pdb) c Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Post-mortem debugging with triple verbosity >>> sys.stdin = Input('p x\nc') >>> sys.argv = 'test --layer samplelayers.Layer1$ -vvv -D'.split() >>> try: testrunner.run_internal(defaults) ... finally: sys.stdin = real_stdin Running tests at level 1 Running samplelayers.Layer1 tests: Set up samplelayers.Layer1 in 0.000 seconds. Running: test_x1 (sampletestsf.TestA1...) (0.000 s) test_y0 (sampletestsf.TestA1...) (0.000 s) test_z0 (sampletestsf.TestA1...) (0.000 s) test_x0 (sampletestsf.TestB1...) (0.000 s) test_y1 (sampletestsf.TestB1...) (0.000 s) test_z0 (sampletestsf.TestB1...) (0.000 s) test_1 (sampletestsf.TestNotMuch1...) (0.000 s) test_2 (sampletestsf.TestNotMuch1...) (0.000 s) test_3 (sampletestsf.TestNotMuch1...) (0.000 s) Ran 9 tests with 0 failures, 0 errors and 0 skipped in 0.001 seconds. Tearing down left over layers: Tear down samplelayers.Layer1 in 0.000 seconds. False Test Suites with None for suites or tests ----------------------------------------- >>> sys.argv = ['test', ... '--tests-pattern', '^sampletests_none_suite$', ... ] >>> testrunner.run_internal(defaults) Test-module import failures: Module: sample1.sampletests_none_suite Traceback (most recent call last): TypeError: Invalid test_suite, None, in sample1.sampletests_none_suite Test-modules with import problems: sample1.sampletests_none_suite Total: 0 tests, 0 failures, 1 errors and 0 skipped in N.NNN seconds. True >>> sys.argv = ['test', ... '--tests-pattern', '^sampletests_none_test$', ... ] >>> testrunner.run_internal(defaults) Test-module import failures: Module: sample1.sampletests_none_test Traceback (most recent call last): TypeError: ... Test-modules with import problems: sample1.sampletests_none_test Total: 0 tests, 0 failures, 1 errors and 0 skipped in N.NNN seconds. True You must use --repeat with --report-refcounts --------------------------------------------- It is an error to specify --report-refcounts (-r) without specifying a repeat count greater than 1 >>> sys.argv = 'test -r'.split() >>> testrunner.run_internal(defaults) You must use the --repeat (-N) option to specify a repeat count greater than 1 when using the --report_refcounts (-r) option. True >>> sys.argv = 'test -r -N1'.split() >>> testrunner.run_internal(defaults) You must use the --repeat (-N) option to specify a repeat count greater than 1 when using the --report_refcounts (-r) option. True Selection --------- Several tests can be excluded using the '!' notation: >>> sys.argv = 'test -u -vv -ssample1.sample13 -t!test_x -t!test_y'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_z0 (sample1.sample13.sampletests.TestA...) test_z0 (sample1.sample13.sampletests.TestB...) test_1 (sample1.sample13.sampletests.TestNotMuch...) test_2 (sample1.sample13.sampletests.TestNotMuch...) test_3 (sample1.sample13.sampletests.TestNotMuch...) test_z1 (sample1.sample13.sampletests) testrunner-ex/sample1/sample13/../../sampletests.rst Ran 7 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Requiring unique test IDs ------------------------- The --require-unique option causes the test runner to require that all test IDs be unique. Its behaviour is tested in zope.testrunner.tests.test_find; here we check its interaction with other options. The --require-unique option does not issue any warnings on its own. >>> sys.argv = 'test --require-unique'.split() >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: ... Attempting to use both --module and --require-unique issues a warning. >>> sys.argv = 'test --module sample --require-unique'.split() >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS You specified a module along with --require-unique; --require-unique will not try to enforce test ID uniqueness when working with a specific module. Running zope.testrunner.layer.UnitTests tests: ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-errors.rst0000644000076600000240000010233415011314374025732 0ustar00m.howitzstaffErrors and Failures =================== Let's look at tests that have errors and failures, first we need to make a temporary copy of the entire testing directory (except .svn files which may be read only): >>> import os.path, sys, tempfile, shutil >>> tmpdir = tempfile.mkdtemp() >>> directory_with_tests = os.path.join(tmpdir, 'testrunner-ex') >>> source = os.path.join(this_directory, 'testrunner-ex') >>> n = len(source) + 1 >>> for root, dirs, files in os.walk(source): ... dirs[:] = [d for d in dirs if d != ".svn"] # prune cruft ... os.mkdir(os.path.join(directory_with_tests, root[n:])) ... for f in files: ... _ = shutil.copy(os.path.join(root, f), ... os.path.join(directory_with_tests, root[n:], f)) >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test --tests-pattern ^sampletests(f|_e|_f)?$ '.split() >>> testrunner.run_internal(defaults) ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: ... Failure in test eek (sample2.sampletests_e) Failed doctest test for sample2.sampletests_e.eek File "testrunner-ex/sample2/sampletests_e.py", line 28, in eek ... File "testrunner-ex/sample2/sampletests_e.py", line 30, in sample2.sampletests_e.eek Failed example: f() Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? f() File "testrunner-ex/sample2/sampletests_e.py", line 19, in f g() File "testrunner-ex/sample2/sampletests_e.py", line 24, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined Error in test test3 (sample2.sampletests_e.Test...) Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_e.py", line 43, in test3 f() File "testrunner-ex/sample2/sampletests_e.py", line 19, in f g() File "testrunner-ex/sample2/sampletests_e.py", line 24, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined Failure in test testrunner-ex/sample2/e.rst Failed doctest test for e.rst File "testrunner-ex/sample2/e.rst", line 0 ... File "testrunner-ex/sample2/e.rst", line 4, in e.rst Failed example: f() Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? f() File "", line 2, in f return x NameError: name 'x' is not defined Failure in test test (sample2.sampletests_f.Test...) Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_f.py", line 21, in test self.assertEqual(1, 0) File "/usr/local/python/2.3/lib/python2.3/unittest.py", line 302, in failUnlessEqual raise self.failureException, \ AssertionError: 1 != 0 Ran 164 tests with 3 failures, 1 errors and 0 skipped in N.NNN seconds. ... Total: 329 tests, 3 failures, 1 errors and 0 skipped in N.NNN seconds. True We see that we get an error report and a traceback for the failing test. In addition, the test runner returned True, indicating that there was an error. If we ask for verbosity, the dotted output will be interrupted, and there'll be a summary of the errors at the end of the test: >>> sys.argv = 'test --tests-pattern ^sampletests(f|_e|_f)?$ -uv'.split() >>> testrunner.run_internal(defaults) ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: ... Running: ................................................................................................. Failure in test eek (sample2.sampletests_e) Failed doctest test for sample2.sampletests_e.eek File "testrunner-ex/sample2/sampletests_e.py", line 28, in eek ... File "testrunner-ex/sample2/sampletests_e.py", line 30, in sample2.sampletests_e.eek Failed example: f() Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? f() File "testrunner-ex/sample2/sampletests_e.py", line 19, in f g() File "testrunner-ex/sample2/sampletests_e.py", line 24, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined ... Error in test test3 (sample2.sampletests_e.Test...) Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_e.py", line 43, in test3 f() File "testrunner-ex/sample2/sampletests_e.py", line 19, in f g() File "testrunner-ex/sample2/sampletests_e.py", line 24, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined ... Failure in test testrunner-ex/sample2/e.rst Failed doctest test for e.rst File "testrunner-ex/sample2/e.rst", line 0 ... File "testrunner-ex/sample2/e.rst", line 4, in e.rst Failed example: f() Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? f() File "", line 2, in f return x NameError: name 'x' is not defined . Failure in test test (sample2.sampletests_f.Test...) Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_f.py", line 21, in test self.assertEqual(1, 0) File ".../unittest.py", line 302, in failUnlessEqual raise self.failureException, \ AssertionError: 1 != 0 ................................................................................................ Ran 164 tests with 3 failures, 1 errors and 0 skipped in 0.040 seconds. ... Tests with errors: test3 (sample2.sampletests_e.Test...) Tests with failures: eek (sample2.sampletests_e) testrunner-ex/sample2/e.rst test (sample2.sampletests_f.Test...) True Similarly for progress output, the progress ticker will be interrupted: >>> sys.argv = ('test --tests-pattern ^sampletests(f|_e|_f)?$ -u -ssample2' ... ' -p').split() >>> testrunner.run_internal(defaults) ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: 1/47 (2.1%) Failure in test eek (sample2.sampletests_e) Failed doctest test for sample2.sampletests_e.eek File "testrunner-ex/sample2/sampletests_e.py", line 28, in eek ... File "testrunner-ex/sample2/sampletests_e.py", line 30, in sample2.sampletests_e.eek Failed example: f() Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? f() File "testrunner-ex/sample2/sampletests_e.py", line 19, in f g() File "testrunner-ex/sample2/sampletests_e.py", line 24, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined 2/47 (4.3%)\r \r 3/47 (6.4%)\r \r 4/47 (8.5%) Error in test test3 (sample2.sampletests_e.Test...) Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_e.py", line 43, in test3 f() File "testrunner-ex/sample2/sampletests_e.py", line 19, in f g() File "testrunner-ex/sample2/sampletests_e.py", line 24, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined 5/47 (10.6%)\r \r 6/47 (12.8%)\r \r 7/47 (14.9%) Failure in test testrunner-ex/sample2/e.rst Failed doctest test for e.rst File "testrunner-ex/sample2/e.rst", line 0 ... File "testrunner-ex/sample2/e.rst", line 4, in e.rst Failed example: f() Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? f() File "", line 2, in f return x NameError: name 'x' is not defined 8/47 (17.0%) Failure in test test (sample2.sampletests_f.Test...) Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_f.py", line 21, in test self.assertEqual(1, 0) File ".../unittest.py", line 302, in failUnlessEqual raise self.failureException, \ AssertionError: 1 != 0 9/47 (19.1%)\r \r 10/47 (21.3%)\r \r 11/47 (23.4%)\r \r 12/47 (25.5%)\r \r 13/47 (27.7%)\r \r 14/47 (29.8%)\r \r 15/47 (31.9%)\r \r 16/47 (34.0%)\r \r 17/47 (36.2%)\r \r 18/47 (38.3%)\r \r 19/47 (40.4%)\r \r 20/47 (42.6%)\r \r 21/47 (44.7%)\r \r 22/47 (46.8%)\r \r 23/47 (48.9%)\r \r 24/47 (51.1%)\r \r 25/47 (53.2%)\r \r 26/47 (55.3%)\r \r 27/47 (57.4%)\r \r 28/47 (59.6%)\r \r 29/47 (61.7%)\r \r 30/47 (63.8%)\r \r 31/47 (66.0%)\r \r 32/47 (68.1%)\r \r 33/47 (70.2%)\r \r 34/47 (72.3%)\r \r 35/47 (74.5%)\r \r 36/47 (76.6%)\r \r 37/47 (78.7%)\r \r 38/47 (80.9%)\r \r 39/47 (83.0%)\r \r 40/47 (85.1%)\r \r 41/47 (87.2%)\r \r 42/47 (89.4%)\r \r 43/47 (91.5%)\r \r 44/47 (93.6%)\r \r 45/47 (95.7%)\r \r 46/47 (97.9%)\r \r 47/47 (100.0%)\r \r Ran 47 tests with 3 failures, 1 errors and 0 skipped in 0.054 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True If you also want a summary of errors at the end, ask for verbosity as well as progress output. Capturing output ---------------- We can ask for output on stdout and stderr to be buffered, and emitted only for failing and erroring tests. >>> sys.argv = ( ... 'test -vv --buffer -ssample2' ... ' --tests-pattern ^stdstreamstest$'.split()) >>> orig_stderr = sys.stderr >>> sys.stderr = sys.stdout >>> testrunner.run_internal(defaults) # doctest: +NORMALIZE_WHITESPACE Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_stderr_error (sample2.stdstreamstest.Test...) Error in test test_stderr_error (sample2.stdstreamstest.Test...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stderr_error raise Exception("boom") Exception: boom Stderr: stderr output on error stderr buffer output on error test_stderr_failure (sample2.stdstreamstest.Test...) Failure in test test_stderr_failure (sample2.stdstreamstest.Test...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stderr_failure self.assertTrue(False) AssertionError: False is not true Stderr: stderr output on failure stderr buffer output on failure test_stderr_success (sample2.stdstreamstest.Test...) test_stdout_error (sample2.stdstreamstest.Test...) Error in test test_stdout_error (sample2.stdstreamstest.Test...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stdout_error raise Exception("boom") Exception: boom Stdout: stdout output on error stdout buffer output on error test_stdout_failure (sample2.stdstreamstest.Test...) Failure in test test_stdout_failure (sample2.stdstreamstest.Test...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stdout_failure self.assertTrue(False) AssertionError: False is not true Stdout: stdout output on failure stdout buffer output on failure test_stdout_success (sample2.stdstreamstest.Test...) Ran 6 tests with 2 failures, 2 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Tests with errors: test_stderr_error (sample2.stdstreamstest.Test...) test_stdout_error (sample2.stdstreamstest.Test...) Tests with failures: test_stderr_failure (sample2.stdstreamstest.Test...) test_stdout_failure (sample2.stdstreamstest.Test...) True >>> sys.stderr = orig_stderr Suppressing multiple doctest errors ----------------------------------- Often, when a doctest example fails, the failure will cause later examples in the same test to fail. Each failure is reported: >>> sys.argv = 'test --tests-pattern ^sampletests_1$'.split() >>> testrunner.run_internal(defaults) # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test eek (sample2.sampletests_1) Failed doctest test for sample2.sampletests_1.eek File "testrunner-ex/sample2/sampletests_1.py", line 17, in eek ... File "testrunner-ex/sample2/sampletests_1.py", line 19, in sample2.sampletests_1.eek Failed example: x = y Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? x = y NameError: name 'y' is not defined ... File "testrunner-ex/sample2/sampletests_1.py", line 21, in sample2.sampletests_1.eek Failed example: x Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? x NameError: name 'x' is not defined ... File "testrunner-ex/sample2/sampletests_1.py", line 24, in sample2.sampletests_1.eek Failed example: z = x + 1 Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? z = x + 1 NameError: name 'x' is not defined Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.002 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True This can be a bit confusing, especially when there are enough tests that they scroll off a screen. Often you just want to see the first failure. This can be accomplished with the -1 option (for "just show me the first failed example in a doctest" :) >>> sys.argv = 'test --tests-pattern ^sampletests_1$ -1'.split() >>> testrunner.run_internal(defaults) # doctest: Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test eek (sample2.sampletests_1) Failed doctest test for sample2.sampletests_1.eek File "testrunner-ex/sample2/sampletests_1.py", line 17, in eek ... File "testrunner-ex/sample2/sampletests_1.py", line 19, in sample2.sampletests_1.eek Failed example: x = y Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? x = y NameError: name 'y' is not defined Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.001 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True The --hide-secondary-failures option is an alias for -1: >>> sys.argv = ( ... 'test --tests-pattern ^sampletests_1$' ... ' --hide-secondary-failures' ... ).split() >>> testrunner.run_internal(defaults) # doctest: Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test eek (sample2.sampletests_1) Failed doctest test for sample2.sampletests_1.eek File "testrunner-ex/sample2/sampletests_1.py", line 17, in eek ... File "testrunner-ex/sample2/sampletests_1.py", line 19, in sample2.sampletests_1.eek Failed example: x = y Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? x = y NameError: name 'y' is not defined Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.001 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True The --show-secondary-failures option counters -1 (or it's alias), causing the second and subsequent errors to be shown. This is useful if -1 is provided by a test script by inserting it ahead of command-line options in sys.argv. >>> sys.argv = ( ... 'test --tests-pattern ^sampletests_1$' ... ' --hide-secondary-failures --show-secondary-failures' ... ).split() >>> testrunner.run_internal(defaults) # doctest: +NORMALIZE_WHITESPACE Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test eek (sample2.sampletests_1) Failed doctest test for sample2.sampletests_1.eek File "testrunner-ex/sample2/sampletests_1.py", line 17, in eek ... File "testrunner-ex/sample2/sampletests_1.py", line 19, in sample2.sampletests_1.eek Failed example: x = y Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? x = y NameError: name 'y' is not defined ... File "testrunner-ex/sample2/sampletests_1.py", line 21, in sample2.sampletests_1.eek Failed example: x Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? x NameError: name 'x' is not defined ... File "testrunner-ex/sample2/sampletests_1.py", line 24, in sample2.sampletests_1.eek Failed example: z = x + 1 Exception raised: Traceback (most recent call last): File ".../doctest.py", line 1256, in __run compileflags, 1) in test.globs File "", line 1, in ? z = x + 1 NameError: name 'x' is not defined Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.002 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True Getting diff output for doctest failures ---------------------------------------- If a doctest has large expected and actual output, it can be hard to see differences when expected and actual output differ. The --ndiff, --udiff, and --cdiff options can be used to get diff output of various kinds. >>> sys.argv = 'test --tests-pattern ^pledge$'.split() >>> _ = testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test pledge (pledge) Failed doctest test for pledge.pledge File "testrunner-ex/pledge.py", line 24, in pledge ... File "testrunner-ex/pledge.py", line 26, in pledge.pledge Failed example: print_pledge() Expected: I give my pledge, as an earthling, to save, and faithfully, to defend from waste, the natural resources of my planet. It's soils, minerals, forests, waters, and wildlife. Got: I give my pledge, as and earthling, to save, and faithfully, to defend from waste, the natural resources of my planet. It's soils, minerals, forests, waters, and wildlife. Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.002 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Here, the actual output uses the word "and" rather than the word "an", but it's a bit hard to pick this out. We can use the various diff outputs to see this better. We could modify the test to ask for diff output, but it's easier to use one of the diff options. The --ndiff option requests a diff using Python's ndiff utility. This is the only method that marks differences within lines as well as across lines. For example, if a line of expected output contains digit 1 where actual output contains letter l, a line is inserted with a caret marking the mismatching column positions. >>> sys.argv = 'test --tests-pattern ^pledge$ --ndiff'.split() >>> _ = testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test pledge (pledge) Failed doctest test for pledge.pledge File "testrunner-ex/pledge.py", line 24, in pledge ... File "testrunner-ex/pledge.py", line 26, in pledge.pledge Failed example: print_pledge() Differences (ndiff with -expected +actual): - I give my pledge, as an earthling, + I give my pledge, as and earthling, ? + to save, and faithfully, to defend from waste, the natural resources of my planet. It's soils, minerals, forests, waters, and wildlife. Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.003 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. The -udiff option requests a standard "unified" diff: >>> sys.argv = 'test --tests-pattern ^pledge$ --udiff'.split() >>> _ = testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test pledge (pledge) Failed doctest test for pledge.pledge File "testrunner-ex/pledge.py", line 24, in pledge ... File "testrunner-ex/pledge.py", line 26, in pledge.pledge Failed example: print_pledge() Differences (unified diff with -expected +actual): @@ -1,3 +1,3 @@ -I give my pledge, as an earthling, +I give my pledge, as and earthling, to save, and faithfully, to defend from waste, the natural resources of my planet. Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.002 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. The -cdiff option requests a standard "context" diff: >>> sys.argv = 'test --tests-pattern ^pledge$ --cdiff'.split() >>> _ = testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Failure in test pledge (pledge) Failed doctest test for pledge.pledge File "testrunner-ex/pledge.py", line 24, in pledge ... File "testrunner-ex/pledge.py", line 26, in pledge.pledge Failed example: print_pledge() Differences (context diff with expected followed by actual): *************** *** 1,3 **** ! I give my pledge, as an earthling, to save, and faithfully, to defend from waste, the natural resources of my planet. --- 1,3 ---- ! I give my pledge, as and earthling, to save, and faithfully, to defend from waste, the natural resources of my planet. Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.002 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Specifying more than one diff option at once causes an error: >>> sys.argv = 'test --tests-pattern ^pledge$ --cdiff --udiff'.split() >>> _ = testrunner.run_internal(defaults) Traceback (most recent call last): ... SystemExit: 1 >>> sys.argv = 'test --tests-pattern ^pledge$ --cdiff --ndiff'.split() >>> _ = testrunner.run_internal(defaults) Traceback (most recent call last): ... SystemExit: 1 >>> sys.argv = 'test --tests-pattern ^pledge$ --udiff --ndiff'.split() >>> _ = testrunner.run_internal(defaults) Traceback (most recent call last): ... SystemExit: 1 Testing-Module Import Errors ---------------------------- If there are errors when importing a test module, these errors are reported. In order to illustrate a module with a syntax error, we create one now: this module used to be checked in to the project, but then it was included in distributions of projects using zope.testrunner too, and distutils complained about the syntax error when it compiled Python files during installation of such projects. So first we create a module with bad syntax: >>> badsyntax_path = os.path.join(directory_with_tests, ... "sample2", "sampletests_i.py") >>> f = open(badsyntax_path, "w") >>> print("importx unittest", file=f) # syntax error >>> f.close() Then run the tests: >>> sys.argv = ('test --tests-pattern ^sampletests(f|_i)?$ --layer 1 ' ... ).split() >>> testrunner.run_internal(defaults) ... # doctest: +NORMALIZE_WHITESPACE Test-module import failures: Module: sample2.sampletests_i Traceback (most recent call last): File "testrunner-ex/sample2/sampletests_i.py", line 1 importx unittest ...^ SyntaxError: invalid syntax... Module: sample2.sample21.sampletests_i Traceback (most recent call last): File "testrunner-ex/sample2/sample21/sampletests_i.py", line 15, in ? import zope.testrunner.huh # noqa: F401 ModuleNotFoundError: No module named 'zope.testrunner.huh' Module: sample2.sample23.sampletests_i Traceback (most recent call last): File "testrunner-ex/sample2/sample23/sampletests_i.py", line 18, in ? class Test(unittest.TestCase): File "testrunner-ex/sample2/sample23/sampletests_i.py", line 23, in Test raise TypeError('eek') TypeError: eek Running samplelayers.Layer1 tests: Set up samplelayers.Layer1 in 0.000 seconds. Ran 9 tests with 0 failures, 3 errors and 0 skipped in 0.000 seconds. Running samplelayers.Layer11 tests: Set up samplelayers.Layer11 in 0.000 seconds. Ran 26 tests with 0 failures, 3 errors and 0 skipped in 0.007 seconds. Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in 0.000 seconds. Set up samplelayers.Layer111 in 0.000 seconds. Ran 26 tests with 0 failures, 3 errors and 0 skipped in 0.007 seconds. Running samplelayers.Layer112 tests: Tear down samplelayers.Layer111 in 0.000 seconds. Set up samplelayers.Layer112 in 0.000 seconds. Ran 26 tests with 0 failures, 3 errors and 0 skipped in 0.007 seconds. Running samplelayers.Layer12 tests: Tear down samplelayers.Layer112 in 0.000 seconds. Tear down samplelayers.Layerx in 0.000 seconds. Tear down samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Ran 26 tests with 0 failures, 3 errors and 0 skipped in 0.007 seconds. Running samplelayers.Layer121 tests: Set up samplelayers.Layer121 in 0.000 seconds. Ran 26 tests with 0 failures, 3 errors and 0 skipped in 0.007 seconds. Running samplelayers.Layer122 tests: Tear down samplelayers.Layer121 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Ran 26 tests with 0 failures, 3 errors and 0 skipped in 0.006 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. Test-modules with import problems: sample2.sampletests_i sample2.sample21.sampletests_i sample2.sample23.sampletests_i Total: 165 tests, 0 failures, 3 errors and 0 skipped in N.NNN seconds. True Reporting Errors to Calling Processes ------------------------------------- The testrunner returns the error status, indicating that the tests failed. This can be useful for an invoking process that wants to monitor the result of a test run. This is applied when invoking the testrunner using the ``run()`` function instead of ``run_internal()``: >>> sys.argv = ( ... 'test --tests-pattern ^sampletests_1$'.split()) >>> try: ... testrunner.run(defaults) ... except SystemExit as e: ... print('exited with code', e.code) ... else: ... print('sys.exit was not called') ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. ... Ran 1 tests with 1 failures, 0 errors and 0 skipped in 0.002 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. exited with code 1 Passing tests exit with code 0 according to UNIX practices: >>> sys.argv = ( ... 'test --tests-pattern ^sampletests$'.split()) >>> try: ... testrunner.run(defaults) ... except SystemExit as e2: ... print('exited with code', e2.code) ... else: ... print('sys.exit was not called') ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: ... Total: 286 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. exited with code 0 And remove the temporary directory: >>> shutil.rmtree(tmpdir) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8633375 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/0000755000076600000240000000000015011314376024277 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/README.rst0000644000076600000240000000015015011314374025760 0ustar00m.howitzstaffThis directory and its subdirectories contain example tests for testing the test runner, testrunner.py. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/brokenlayer.py0000644000076600000240000000306415011314374027167 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2012-2018 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with layers that have broken set up and tear down.""" import unittest class BrokenSetUpLayer: @classmethod def setUp(cls): raise ValueError('No value is good enough for me!') @classmethod def tearDown(cls): pass class BrokenTearDownLayer: @classmethod def setUp(cls): pass @classmethod def tearDown(cls): raise TypeError('You are not my type. No-one is my type!') class TestSomething1(unittest.TestCase): layer = BrokenSetUpLayer def test_something(self): pass class TestSomething2(unittest.TestCase): layer = BrokenTearDownLayer def test_something(self): pass def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething1)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething2)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/gc-after-test.py0000644000076600000240000000252215011314374027315 0ustar00m.howitzstafffrom sys import exc_info from unittest import TestCase from warnings import warn class GcAfterTestTests(TestCase): def tearDown(self): try: del self.cycle except AttributeError: pass def test_okay(self): pass def test_cycle_without_resource(self): self.cycle = _Cycle() def test_cycle_with_resource(self): self.cycle = _Cycle(resource=_Resource()) def test_test_holds_cycle(self): self.hold_cycle = _Cycle(resource=_Resource()) def test_failure(self): raise AssertionError("failure") def test_exception(self): 1 / 0 def test_traceback_cycle(self): def f(): try: 1 / 0 except Exception: # create cycle tb = exc_info()[2] # noqa: F841 f() class _Cycle: """Auxiliary class creating a reference cycle.""" def __init__(self, **kw): self.self = self # create reference cycle self.__dict__.update(kw) class _Resource: """Auxiliary class emulating a resource.""" closed = False def close(self): self.closed = True def __del__(self): if not self.closed: warn(ResourceWarning( "not closed" " - this is no error: testing ResourceWarning here")) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/gc0.py0000644000076600000240000000145115011314374025321 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest def make_sure_gc_is_disabled(): """ >>> import gc >>> gc.get_threshold()[0] 0 """ def test_suite(): return doctest.DocTestSuite() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/gc1.py0000644000076600000240000000145615011314374025327 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest def make_sure_gc_threshold_is_one(): """ >>> import gc >>> gc.get_threshold()[0] 1 """ def test_suite(): return doctest.DocTestSuite() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/gcset.py0000644000076600000240000000220215011314374025750 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import gc # for some early 3.13 versions, the third threshold has been # a constant 0; this was changed again for newer versions _thresholds = gc.get_threshold() gc.set_threshold(10, 10, 10) ZERO_THR3 = gc.get_threshold()[2] == 0 gc.set_threshold(*_thresholds) def make_sure_gc_threshold_is_701_11_9(): pass make_sure_gc_threshold_is_701_11_9.__doc__ = """\ >>> import gc >>> gc.get_threshold() (701, 11, %d) """ % (0 if ZERO_THR3 else 9) def test_suite(): return doctest.DocTestSuite() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/gcstats.py0000644000076600000240000000150415011314374026317 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest def generate_some_gc_statistics(): """ >>> import gc >>> l = []; l.append(l); del l >>> _ = gc.collect() """ def test_suite(): return doctest.DocTestSuite() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/leak.py0000644000076600000240000000220615011314374025563 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import time import unittest class ClassicLeakable: def __init__(self): self.x = 'x' class Leakable: def __init__(self): self.x = 'x' leaked = [] class TestSomething(unittest.TestCase): def testleak(self): leaked.append((ClassicLeakable(), Leakable(), time.time())) def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/new_threads.py0000644000076600000240000000056415011314374027157 0ustar00m.howitzstaffimport threading import time import unittest class Mythread(threading.Thread): # The default includes a timestamp when the thread was started. def __repr__(self): return "" % self.name class TestNewThreadsReporting(unittest.TestCase): def test_leave_thread_behind(self): Mythread(name='t1', target=time.sleep, args=[1]).start() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/pledge.py0000644000076600000240000000233215011314374026107 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest pledge_template = """\ I give my pledge, as %s, to save, and faithfully, to defend from waste, the natural resources of my %s. It's soils, minerals, forests, waters, and wildlife. """ def pledge(): """ >>> def print_pledge(): ... print(pledge_template % ('and earthling', 'planet')) >>> print_pledge() I give my pledge, as an earthling, to save, and faithfully, to defend from waste, the natural resources of my planet. It's soils, minerals, forests, waters, and wildlife. """ def test_suite(): return doctest.DocTestSuite() ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1747294461.866862 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/0000755000076600000240000000000015011314376025641 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/__init__.py0000644000076600000240000000000215011314374027740 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8671331 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample11/0000755000076600000240000000000015011314376027264 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample11/__init__.py0000644000076600000240000000000215011314374031363 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample11/sampletests.py0000644000076600000240000000561615011314374032210 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestA3(unittest.TestCase): level = 3 def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestB2(unittest.TestCase): level = 2 def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA3)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB2)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8672707 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample12/0000755000076600000240000000000015011314376027265 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample12/__init__.py0000644000076600000240000000000215011314374031364 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8675508 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample13/0000755000076600000240000000000015011314376027266 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample13/__init__.py0000644000076600000240000000000215011314374031365 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sample13/sampletests.py0000644000076600000240000000415715011314374032211 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8691578 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/0000755000076600000240000000000015011314376030205 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/__init__.py0000644000076600000240000000000215011314374032304 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test1.py0000644000076600000240000000415715011314374031624 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test11.py0000644000076600000240000000724615011314374031707 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer11' layer = samplelayers.Layer11 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test111.py0000644000076600000240000000725015011314374031763 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer111' layer = samplelayers.Layer111 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test112.py0000644000076600000240000000725115011314374031765 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer112' layer = samplelayers.Layer112 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test12.py0000644000076600000240000000724615011314374031710 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer12' layer = samplelayers.Layer12 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test121.py0000644000076600000240000000725015011314374031764 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer121' layer = samplelayers.Layer121 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test122.py0000644000076600000240000000725015011314374031765 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer122' layer = samplelayers.Layer122 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test_one.py0000644000076600000240000000415715011314374032404 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_discover.py0000644000076600000240000000015215011314374032451 0ustar00m.howitzstaffimport unittest class TestA(unittest.TestCase): def test_truth(self): self.assertTrue(True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_discover_notests.py0000644000076600000240000000011315011314374034225 0ustar00m.howitzstaffdef test_function_that_would_never_be_run(self): self.assertTrue(True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_none_suite.py0000644000076600000240000000132215011314374033003 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ def test_suite(): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_none_test.py0000644000076600000240000000144415011314374032636 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ import unittest def test_suite(): suite = unittest.TestSuite() suite.addTest(None) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_ntd.py0000644000076600000240000000226315011314374031425 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ import unittest class Layer: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class TestSomething(unittest.TestCase): layer = Layer def test_something(self): pass def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_ntds.py0000644000076600000240000000226315011314374031610 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ import unittest class Layer: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class TestSomething(unittest.TestCase): layer = Layer def test_something(self): pass def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample1/sampletestsf.py0000644000076600000240000000415415011314374030727 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8705754 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/0000755000076600000240000000000015011314376025642 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/__init__.py0000644000076600000240000000000215011314374027741 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/badsyntax.py0000644000076600000240000000134115011314374030206 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2018 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # This is an intentional syntax error, to test module import errors. importx unittest # noqa: E999 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8707135 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/do-not-enter/0000755000076600000240000000000015011314376030155 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/do-not-enter/sampletests.py0000644000076600000240000000235115011314374033072 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest def f(): g() def g(): x = 1 x = x + 1 x = y + 1 # noqa: F821 x = x + 1 def eek(self): """ >>> f() 1 """ class Test(unittest.TestCase): def test1(self): pass def test2(self): pass def test3(self): f() def test4(self): pass def test5(self): pass def test_suite(): suite = unittest.TestSuite() suite.addTest(doctest.DocTestSuite()) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(Test)) suite.addTest(doctest.DocFileSuite('e.rst')) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/e.rst0000644000076600000240000000006615011314374026620 0ustar00m.howitzstaff >>> def f(): ... return x >>> f() ././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1747294461.87113 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample21/0000755000076600000240000000000015011314376027266 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample21/__init__.py0000644000076600000240000000000215011314374031365 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample21/sampletests.py0000644000076600000240000000415715011314374032211 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample21/sampletests_i.py0000644000076600000240000000154215011314374032514 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest import zope.testrunner.huh # noqa: F401 class Test(unittest.TestCase): def test(self): self.assertEqual(1, 0) def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8714073 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample22/0000755000076600000240000000000015011314376027267 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample22/__init__.py0000644000076600000240000000000215011314374031366 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample22/sampletests_i.py0000644000076600000240000000147115011314374032516 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): def test(self): self.assertEqual(1, 0) def test_suitex(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8717186 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample23/0000755000076600000240000000000015011314376027270 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample23/__init__.py0000644000076600000240000000000215011314374031367 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sample23/sampletests_i.py0000644000076600000240000000152415011314374032516 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): def test(self): self.assertEqual(1, 0) raise TypeError('eek') def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8721433 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/0000755000076600000240000000000015011314376030206 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/__init__.py0000644000076600000240000000000215011314374032305 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/test_1.py0000644000076600000240000000415715011314374031764 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/testone.py0000644000076600000240000000415715011314374032246 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_1.py0000644000076600000240000000142115011314374030774 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest def eek(self): """ >>> x = y >>> x >>> z = x + 1 """ def test_suite(): return doctest.DocTestSuite() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_e.py0000644000076600000240000000244315011314374031065 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest def f(): g() def g(): x = 1 x = x + 1 __traceback_info__ = "I don't know what Y should be." x = y + 1 # noqa: F821 x = x + 1 def eek(self): """ >>> f() 1 """ class Test(unittest.TestCase): def test1(self): pass def test2(self): pass def test3(self): f() def test4(self): pass def test5(self): pass def test_suite(): suite = unittest.TestSuite() suite.addTest(doctest.DocTestSuite()) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(Test)) suite.addTest(doctest.DocFileSuite('e.rst')) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_f.py0000644000076600000240000000147015011314374031065 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): def test(self): self.assertEqual(1, 0) def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_ntd.py0000644000076600000240000000226315011314374031426 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ import unittest class Layer: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class TestSomething(unittest.TestCase): layer = Layer def test_something(self): pass def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_ntds.py0000644000076600000240000000414115011314374031606 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ import doctest import unittest class Layer: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class TestSomething(unittest.TestCase): layer = Layer def test_something(self): import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found def test_something2(self): import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found def test_something3(self): import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found def test_something4(self): import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found def test_something5(self): f() def f(): import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found def test_set_trace(): """ >>> if 1: ... x = 1 ... import pdb; pdb.set_trace() """ def test_set_trace2(): """ >>> f() """ def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething)) d = doctest.DocTestSuite() d.layer = Layer suite.addTest(d) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/stderrtest.py0000644000076600000240000000247515011314374030425 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that produces output on stderr """ import doctest import sys import unittest class Layer: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): pass tearDown = classmethod(tearDown) def test_something(): """ >>> 1 + 1 2 """ def test_suite(): # Generate some text on stderr to be sure the test runner can handle it. sys.stderr.write('A message on stderr.' ' Please ignore (expected in test output).\n') suite = unittest.TestSuite() d = doctest.DocTestSuite() d.layer = Layer suite.addTest(d) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample2/stdstreamstest.py0000644000076600000240000000417115011314374031306 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests that produce output on stdout and stderr.""" import sys import unittest class Test(unittest.TestCase): def _getStreamBuffer(self, stream): return stream.buffer def test_stdout_success(self): sys.stdout.write("stdout output on success\n") self._getStreamBuffer(sys.stdout).write( b"stdout buffer output on success\n") def test_stdout_failure(self): sys.stdout.write("stdout output on failure\n") self._getStreamBuffer(sys.stdout).write( b"stdout buffer output on failure\n") self.assertTrue(False) def test_stdout_error(self): sys.stdout.write("stdout output on error\n") self._getStreamBuffer(sys.stdout).write( b"stdout buffer output on error\n") raise Exception("boom") def test_stderr_success(self): sys.stderr.write("stderr output on success\n") self._getStreamBuffer(sys.stderr).write( b"stderr buffer output on success\n") def test_stderr_failure(self): sys.stderr.write("stderr output on failure\n") self._getStreamBuffer(sys.stderr).write( b"stderr buffer output on failure\n") self.assertTrue(False) def test_stderr_error(self): sys.stderr.write("stderr output on error\n") self._getStreamBuffer(sys.stderr).write( b"stderr buffer output on error\n") raise Exception("boom") def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8734653 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/0000755000076600000240000000000015011314376025643 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/__init__.py0000644000076600000240000000000215011314374027742 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/post_mortem5.rst0000644000076600000240000000004715011314374031031 0ustar00m.howitzstaff >>> x = 1 >>> raise ValueError ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/post_mortem6.rst0000644000076600000240000000007015011314374031026 0ustar00m.howitzstaff >>> from sample3.sampletests_d import g >>> g() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/post_mortem_failure.rst0000644000076600000240000000003615011314374032451 0ustar00m.howitzstaff >>> x = 1 >>> x 2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8736215 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sample31/0000755000076600000240000000000015011314376027270 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sample31/__init__.py0000644000076600000240000000000215011314374031367 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8737595 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sample32/0000755000076600000240000000000015011314376027271 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sample32/__init__.py0000644000076600000240000000000215011314374031370 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8738987 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sample33/0000755000076600000240000000000015011314376027272 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sample33/__init__.py0000644000076600000240000000000215011314374031371 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sampletests.py0000644000076600000240000000415415011314374030563 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sampletests_d.py0000644000076600000240000000447315011314374031072 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest class TestSomething(unittest.TestCase): def test_set_trace1(self): x = 1 import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found y = x # noqa: F841 def test_set_trace2(self): f() def test_post_mortem1(self): x = 1 # noqa: F841 raise ValueError def test_post_mortem2(self): g() def test_post_mortem_failure1(self): x = 1 y = 2 assert x == y @unittest.skip("skipped test") def test_skipped(self): self.fail('test should have been skipped') def f(): x = 1 import pdb # noqa: T100 import for pdb found pdb.set_trace() # noqa: T100 pdb.set_trace found y = x # noqa: F841 def g(): x = 1 # noqa: F841 raise ValueError def set_trace3(self): """ >>> x = 1 >>> if 1: ... import pdb; pdb.set_trace() ... y = x """ def set_trace4(self): """ >>> f() """ def post_mortem3(self): """ >>> x = 1 >>> raise ValueError """ def post_mortem4(self): """ >>> g() """ def post_mortem_failure2(): """ >>> x = 1 >>> x 2 """ def test_suite(): return unittest.TestSuite(( doctest.DocTestSuite(), unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething), doctest.DocFileSuite('set_trace5.rst'), doctest.DocFileSuite('set_trace6.rst'), doctest.DocFileSuite('post_mortem5.rst'), doctest.DocFileSuite('post_mortem6.rst'), doctest.DocFileSuite('post_mortem_failure.rst'), )) if __name__ == '__main__': unittest.main(defaultTest='test_suite') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/sampletests_ntd.py0000644000076600000240000000274215011314374031431 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with a layer that can't be torn down """ import unittest class Layer: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class TestSomething(unittest.TestCase): layer = Layer def test_something(self): pass def test_something_else(self): pass def test_error1(self): raise TypeError("Can we see errors") def test_error2(self): raise TypeError("I hope so") def test_fail1(self): self.assertEqual(1, 2) def test_fail2(self): self.assertEqual(1, 3) def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/set_trace5.rst0000644000076600000240000000012615011314374030430 0ustar00m.howitzstaff >>> x = 1 >>> if 1: ... import pdb; pdb.set_trace() ... y = x ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sample3/set_trace6.rst0000644000076600000240000000007015011314374030427 0ustar00m.howitzstaff >>> from sample3.sampletests_d import f >>> f() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/samplelayers.py0000644000076600000240000001025715011314374027355 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample test layers """ layer = '0' # Internal to samples. Not part of layer API layerx = '0' class Layer1: # Internal to samples. Not part of layer API: layer = '1' base = '0' layerx = '0' def setUp(self): global layer if layer != self.base: raise ValueError(f"Bad layer, {layer}, for {self}.") layer = self.layer setUp = classmethod(setUp) def tearDown(self): global layer if layer != self.layer: raise ValueError(f"Bad layer, {layer}, for {self}.") layer = self.base tearDown = classmethod(tearDown) class Layerx: layerx = '1' # Internal to samples. Not part of layer API basex = '0' def setUp(self): global layerx if layerx != self.basex: raise ValueError(f"Bad layerx, {layerx}, for {self}.") layerx = self.layerx setUp = classmethod(setUp) def tearDown(self): global layerx if layerx != self.layerx: raise ValueError(f"Bad layerx, {layerx}, for {self}.") layerx = self.basex tearDown = classmethod(tearDown) class Layer11(Layer1): layer = '11' # Internal to samples. Not part of layer API base = '1' # Internal to samples. Not part of layer API class Layer12(Layer1): layer = '12' # Internal to samples. Not part of layer API base = '1' # Internal to samples. Not part of layer API class Layer111(Layerx, Layer11): layer = '111' # Internal to samples. Not part of layer API base = '11' # Internal to samples. Not part of layer API layerx = '2' # Internal to samples. Not part of layer API basex = '1' def setUp(self): global layer if layer != self.base: raise ValueError(f"Bad layer, {layer}, for {self}.") layer = self.layer global layerx if layerx != self.basex: raise ValueError(f"Bad layerx, {layerx}, for {self}.") layerx = self.layerx setUp = classmethod(setUp) def tearDown(self): global layer if layer != self.layer: raise ValueError(f"Bad layer, {layer}, for {self}.") layer = self.base global layerx if layerx != self.layerx: raise ValueError(f"Bad layerx, {layerx}, for {self}.") layerx = self.basex tearDown = classmethod(tearDown) class Layer121(Layer12): layer = '121' # Internal to samples. Not part of layer API base = '12' # Internal to samples. Not part of layer API class Layer112(Layerx, Layer11): layer = '112' # Internal to samples. Not part of layer API base = '11' # Internal to samples. Not part of layer API layerx = '2' # Internal to samples. Not part of layer API basex = '1' def setUp(self): global layer if layer != self.base: raise ValueError(f"Bad layer, {layer}, for {self}.") layer = self.layer global layerx if layerx != self.basex: raise ValueError(f"Bad layerx, {layerx}, for {self}.") layerx = self.layerx setUp = classmethod(setUp) def tearDown(self): global layer if layer != self.layer: raise ValueError(f"Bad layer, {layer}, for {self}.") layer = self.base global layerx if layerx != self.layerx: raise ValueError(f"Bad layerx, {layerx}, for {self}.") layerx = self.basex tearDown = classmethod(tearDown) class Layer122(Layer12): layer = '122' # Internal to samples. Not part of layer API base = '12' # Internal to samples. Not part of layer API ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8751786 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/0000755000076600000240000000000015011314376026643 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/__init__.py0000644000076600000240000000000215011314374030742 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test1.py0000644000076600000240000000415415011314374030257 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test11.py0000644000076600000240000000724315011314374030342 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer11' layer = samplelayers.Layer11 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test111.py0000644000076600000240000000724515011314374030425 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer111' layer = samplelayers.Layer111 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test112.py0000644000076600000240000000757715011314374030436 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer112' layer = samplelayers.Layer112 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 self.clean = getattr(self, 'clean', 0) + 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) # This is a test that the test runner clears attributes # that are set in setUp but not cleared in tearDown. self.assertEqual(self.clean, 1) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test12.py0000644000076600000240000000724315011314374030343 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer12' layer = samplelayers.Layer12 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test121.py0000644000076600000240000000724515011314374030426 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer121' layer = samplelayers.Layer121 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test122.py0000644000076600000240000000724515011314374030427 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers layername = 'samplelayers.Layer122' layer = samplelayers.Layer122 x = 0 y = 0 z = 0 class TestA(unittest.TestCase): layer = layername def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestB(unittest.TestCase): layer = layername def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) class TestNotMuch(unittest.TestCase): layer = layername def test_1(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_2(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def test_3(self): self.assertEqual(samplelayers.layer, layer.layer) self.assertEqual(samplelayers.layerx, layer.layerx) def setUp(test): test.globs['z'] = 1 test.globs['layer'] = layer.layer test.globs['layerx'] = layer.layerx test.globs['samplelayers'] = samplelayers def test_y0(self): """ >>> y = 0 >>> y 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_x0(self): """ >>> x = 0 >>> x 0 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_z1(self): """ >>> z 1 >>> (layer == samplelayers.layer), (layerx == samplelayers.layerx) (True, True) """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) s = doctest.DocTestSuite(setUp=setUp) s.layer = layer suite.addTest(s) s = doctest.DocFileSuite('../sampletestsl.rst', setUp=setUp) s.layer = layer suite.addTest(s) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests/test_one.py0000644000076600000240000000415415011314374031037 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('../sampletests.rst', setUp=setUp)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests.rst0000644000076600000240000000023115011314374027367 0ustar00m.howitzstaffThis is a sample doctest >>> x=1 >>> x 1 Blah blah blah >>> x 1 Blah blah blah >>> x 1 Blah blah blah >>> x 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests_buffering.py0000644000076600000240000000321115011314374031237 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample tests with sleep and layers that can't be torn down """ import time import unittest class Layer1: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class Layer2: def setUp(self): pass setUp = classmethod(setUp) def tearDown(self): raise NotImplementedError tearDown = classmethod(tearDown) class TestSomething1(unittest.TestCase): layer = Layer1 def test_something(self): pass class TestSomething2(unittest.TestCase): layer = Layer2 def test_something(self): time.sleep(0.5) def test_something2(self): time.sleep(0.5) def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething1)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestSomething2)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletests_many.py0000644000076600000240000000407515011314374030245 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2020 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """A large number of sample tests.""" import unittest class Layer1: """A layer that can't be torn down.""" @classmethod def setUp(self): pass @classmethod def tearDown(self): raise NotImplementedError class Layer2: @classmethod def setUp(self): pass @classmethod def tearDown(self): pass class TestNoTeardown(unittest.TestCase): layer = Layer1 def test_something(self): pass def make_TestMany(): attrs = {'layer': Layer2} # Add enough failing test methods to make the concatenation of all their # test IDs (formatted as "test_foo (sampletests_many.TestMany)") # overflow the capacity of a pipe. This is system-dependent, but on # Linux since 2.6.11 it defaults to 65536 bytes, so will overflow by the # time we've written 874 of these test IDs. If the pipe capacity is # much larger than that, then this test might be ineffective. for i in range(1000): attrs['test_some_very_long_test_name_with_padding_%03d' % i] = ( lambda self: self.fail()) return type('TestMany', (unittest.TestCase,), attrs) TestMany = make_TestMany() def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNoTeardown)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestMany)) return suite if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletestsf.py0000644000076600000240000001107515011314374027365 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import unittest import samplelayers x = 0 y = 0 z = 0 class TestA(unittest.TestCase): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestA2(unittest.TestCase): level = 2 def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) def test_y0(self): self.assertEqual(y, 0) def test_z0(self): self.assertEqual(z, 0) class TestB(unittest.TestCase): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) def test_x0(self): self.assertEqual(x, 0) def test_z0(self): self.assertEqual(z, 0) class TestNotMuch(unittest.TestCase): def test_1(self): pass def test_2(self): pass def test_3(self): pass def setUp(test): test.globs['z'] = 1 def test_y0(self): """ >>> y = 0 >>> y 0 """ def test_x0(self): """ >>> x = 0 >>> x 0 """ def test_z1(self): """ >>> z 1 """ class Layered: layer = 'samplelayers.Layer1' layerv = '1' layerx = '0' class TestA1(unittest.TestCase, Layered): def setUp(self): global x x = 1 def tearDown(self): global x x = 0 def test_x1(self): self.assertEqual(x, 1) self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_y0(self): self.assertEqual(y, 0) self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) class TestB1(unittest.TestCase, Layered): def setUp(self): global y y = 1 def tearDown(self): global y y = 0 def test_y1(self): self.assertEqual(y, 1) self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_x0(self): self.assertEqual(x, 0) self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_z0(self): self.assertEqual(z, 0) self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) class TestNotMuch1(unittest.TestCase, Layered): def test_1(self): self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_2(self): self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_3(self): self.assertEqual(samplelayers.layer, self.layerv) self.assertEqual(samplelayers.layerx, self.layerx) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA2)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch)) suite.addTest(doctest.DocTestSuite(setUp=setUp)) suite.addTest(doctest.DocFileSuite('sampletests.rst', setUp=setUp)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestA1)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestB1)) suite.addTest( unittest.defaultTestLoader.loadTestsFromTestCase(TestNotMuch1)) return suite ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/sampletestsl.rst0000644000076600000240000000045715011314374027555 0ustar00m.howitzstaffThis is a sample doctest >>> x=1 >>> x 1 Blah blah blah >>> x 1 Blah blah blah >>> x 1 Blah blah blah >>> x 1 are we in the right laters? >>> import samplelayers >>> layer == samplelayers.layer True >>> layerx == samplelayers.layerx True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/subtest.py0000644000076600000240000000165415011314374026346 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2024 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class TestSomething(unittest.TestCase): def test_subTest(self): with self.subTest("fail 1"): self.assertEqual(0, 1) with self.subTest("success"): self.assertEqual(0, 0) with self.subTest("fail 2"): self.assertEqual(0, 1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/unicode.py0000644000076600000240000000131715011314374026277 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2007 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest def test_suite(): return doctest.DocFileSuite('unicode.rst') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8756018 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/0000755000076600000240000000000015011314376026610 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/README.rst0000644000076600000240000000014015011314374030270 0ustar00m.howitzstaffThe tests in this subtree are trivial, and used only to test testrunner's --usecompiled option. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/__init__.py0000644000076600000240000000003015011314374030710 0ustar00m.howitzstaff# Makes this a package. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/compiletest.py0000644000076600000240000000165015011314374031512 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): def test1(self): self.assertEqual(1, 1) def test2(self): self.assertEqual(1, 1) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(Test)) return suite ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8758953 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/package/0000755000076600000240000000000015011314376030203 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/package/__init__.py0000644000076600000240000000003015011314374032303 0ustar00m.howitzstaff# Makes this a package. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex/usecompiled/package/compiletest.py0000644000076600000240000000165015011314374033105 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): def test1(self): self.assertEqual(1, 1) def test2(self): self.assertEqual(1, 1) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(Test)) return suite ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8369591 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/0000755000076600000240000000000015011314376025051 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000003200000000000010210 xustar0026 mtime=1747294461.83701 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/0000755000076600000240000000000015011314376025776 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8634794 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/0000755000076600000240000000000015011314376031236 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1747294461.863608 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/0000755000076600000240000000000015011314376032021 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000021500000000000010213 xustar00119 path=zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/__init__.py 22 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/__0000644000076600000240000000000015011314374032305 0ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00112 path=zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/bar/ 27 mtime=1747294461.863866 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/ba0000755000076600000240000000000015011314376032324 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000022100000000000010210 xustar00123 path=zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/bar/__init__.py 22 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/ba0000644000076600000240000000001215011314374032315 0ustar00m.howitzstaff# package ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00120 path=zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/bar/tests.py 22 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/ba0000644000076600000240000000001015011314374032313 0ustar00m.howitzstaff# tests ././@PaxHeader0000000000000000000000000000020500000000000010212 xustar00111 path=zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/test.py 22 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/test.p0000644000076600000240000000000015011314374032362 0ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8642967 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-37/0000755000076600000240000000000015011314376024526 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-37/layers.py0000644000076600000240000000125615011314374026401 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2016 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## class LayerA: pass class LayerB: pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-37/stop_on_error.py0000644000076600000240000000160315011314374027770 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2016 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class ErrorTestCase1(unittest.TestCase): layer = "layers.LayerA" def test(self): self.assertTrue(False) class ErrorTestCase2(unittest.TestCase): layer = "layers.LayerB" def test(self): self.assertTrue(False) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-37/stop_on_failure.py0000644000076600000240000000173515011314374030274 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2016 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class FailureTestCase1(unittest.TestCase): layer = "layers.LayerA" def test(self): # We want to have an error, not a failure raise Exception class FailureTestCase2(unittest.TestCase): layer = "layers.LayerB" def test(self): # We want to have an error, not a failure raise Exception ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8644376 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-6/0000755000076600000240000000000015011314376024442 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-6/cwdtests.py0000644000076600000240000000044515011314374026655 0ustar00m.howitzstaffimport os import unittest class Layer1: pass class Layer2: pass class Test1(unittest.TestCase): layer = Layer1 def test_that_chdirs(self): os.chdir(os.path.dirname(__file__)) class Test2(unittest.TestCase): layer = Layer2 def test(self): pass ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1747294461.864579 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-expectedFailure/0000755000076600000240000000000015011314376027406 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00118 path=zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-expectedFailure/sample_expected_failure_tests.py 22 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-expectedFailure/sample_expected_failure_0000644000076600000240000000164215011314374034342 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2011 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class TestExpectedFailures(unittest.TestCase): @unittest.expectedFailure def test_expected_failure(self): self.fail('test fail, as expected') @unittest.expectedFailure def test_unexpected_success(self): self.assertTrue('unexpected success') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8377378 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-lib/0000755000076600000240000000000015011314376025460 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8647146 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-lib/sample4/0000755000076600000240000000000015011314376027025 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-lib/sample4/__init__.py0000644000076600000240000000000215011314374031124 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8648574 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-lib/sample4/products/0000755000076600000240000000000015011314376030670 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-lib/sample4/products/__init__.py0000644000076600000240000000177315011314374033007 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample package that knits in extra directories. """ import os __path__.append( os.path.join( os.path.dirname( # testing os.path.dirname( # testrunner-ex-knit-lib os.path.dirname( # sample4 os.path.dirname(__file__) # products ) ) ), "testrunner-ex-pp-products" ) ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8651674 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-products/0000755000076600000240000000000015011314376026555 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-products/__init__.py0000644000076600000240000000000215011314374030654 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8654816 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-products/more/0000755000076600000240000000000015011314376027517 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-products/more/__init__.py0000644000076600000240000000000215011314374031616 0ustar00m.howitzstaff# ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-products/more/sampletests.py0000644000076600000240000000154415011314374032437 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): layer = 'samplelayers.Layer111' def test_another_test_in_products(self): pass def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-pp-products/sampletests.py0000644000076600000240000000154215011314374031473 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class Test(unittest.TestCase): layer = 'samplelayers.Layer111' def test_extra_test_in_products(self): pass def test_suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(Test) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1747294461.865665 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-skip/0000755000076600000240000000000015011314376025243 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-ex-skip/sample_skipped_tests.py0000644000076600000240000000221115011314374032031 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2013 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class TestSkipppedWithLayer(unittest.TestCase): layer = "sample_skipped_tests.Layer" @unittest.skip('Hop, skipped') def test_layer_skipped(self): pass def test_layer_pass(self): pass class Layer: pass class TestSkipppedNoLayer(unittest.TestCase): # Only one test, so we don't have to bother about ordering when the runner # displays the tests' list while in verbose mode. @unittest.skip("I'm a skipped test!") def test_skipped(self): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-expected-failures.rst0000644000076600000240000000477515011314374030041 0ustar00m.howitzstafftestrunner handling of expected failures ======================================== >>> import os, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex-expectedFailure') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sample_expected_failure_tests$', ... ] Expected failures are not reported as failures: >>> sys.argv = 'test -t test_expected_failure'.split() >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Unexpected successes are reported as failures: >>> sys.argv = 'test -t test_unexpected_success'.split() >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Error in test test_unexpected_success (sample_expected_failure_tests.TestExpectedFailures...) Traceback (most recent call last): zope.testrunner.runner.UnexpectedSuccess Ran 1 tests with 1 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. True In verbose mode, tests with unexpected failures are listed as failures in the summary: >>> sys.argv = 'test -t test_unexpected_success -vv'.split() >>> testrunner.run_internal(defaults) ... # doctest: +ELLIPSIS Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_unexpected_success (sample_expected_failure_tests.TestExpectedFailures...) Error in test test_unexpected_success (sample_expected_failure_tests.TestExpectedFailures...) Traceback (most recent call last): zope.testrunner.runner.UnexpectedSuccess Ran 1 tests with 1 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Tests with failures: test_unexpected_success (sample_expected_failure_tests.TestExpectedFailures...) True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-gc-after-test.rst0000644000076600000240000001250515011314374027063 0ustar00m.howitzstaffDebugging cyclic garbage and ResourceWarnings ============================================= The --gc-after-test option can be used to detect the creation of cyclic garbage and diagnose ``ResourceWarning``s. Note: Python writes ``ResourceWarning`` messages to ``stderr`` which it not captured by ``doctest``. The sample output below therefore does not show the warnings (even though two are issued). >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', 'gc-after-test', ... ] >>> from zope import testrunner Verbosity level 1 >>> sys.argv = 'test --gc-after-test -v'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: .!.!. Error in test test_exception (gc-after-test.GcAfterTestTests...) Traceback (most recent call last): ... ZeroDivisionError: ... ... Failure in test test_failure (gc-after-test.GcAfterTestTests...) Traceback (most recent call last): ... AssertionError: failure ...!.! Ran 7 tests with 1 failures, 1 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Tests with errors: test_exception (gc-after-test.GcAfterTestTests...) Tests with failures: test_failure (gc-after-test.GcAfterTestTests...) Verbosity level 2 (or higher) >>> sys.argv = 'test --gc-after-test -vv'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_cycle_with_resource (gc-after-test.GcAfterTestTests...) [3] test_cycle_without_resource (gc-after-test.GcAfterTestTests...) [2] test_exception (gc-after-test.GcAfterTestTests...) Error in test test_exception (gc-after-test.GcAfterTestTests...) Traceback (most recent call last): ... ZeroDivisionError: ... ...test_failure (gc-after-test.GcAfterTestTests...) Failure in test test_failure (gc-after-test.GcAfterTestTests...) Traceback (most recent call last): ... AssertionError: failure ...test_okay (gc-after-test.GcAfterTestTests...) test_test_holds_cycle (gc-after-test.GcAfterTestTests...) [3] test_traceback_cycle (gc-after-test.GcAfterTestTests...) [5] Ran 7 tests with 1 failures, 1 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Tests with errors: test_exception (gc-after-test.GcAfterTestTests...) Tests with failures: test_failure (gc-after-test.GcAfterTestTests...) Verbosity level 4 (or higher) Note: starting with Python 3.13, the garbage collector identifies an instance and its ``__dict__``; as a consequence, cycles appear smaller than in preceding versions (not mentioning the involved ``__dict__``s). >>> sys.argv = 'test --gc-after-test -vvvv'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_cycle_with_resource (gc-after-test.GcAfterTestTests...) (N.NNN s) [3] The following test left cyclic garbage behind: test_cycle_with_resource (gc-after-test.GcAfterTestTests...) Cycle 1 * ... test_cycle_without_resource (gc-after-test.GcAfterTestTests...) (N.NNN s) [2] The following test left cyclic garbage behind: test_cycle_without_resource (gc-after-test.GcAfterTestTests...) Cycle 1 * ... test_exception (gc-after-test.GcAfterTestTests...) (N.NNN s) Error in test test_exception (gc-after-test.GcAfterTestTests...) Traceback (most recent call last): ... ZeroDivisionError: ... ...test_failure (gc-after-test.GcAfterTestTests...) (N.NNN s) Failure in test test_failure (gc-after-test.GcAfterTestTests...) Traceback (most recent call last): ... AssertionError: failure ...test_okay (gc-after-test.GcAfterTestTests...) (N.NNN s) test_test_holds_cycle (gc-after-test.GcAfterTestTests...) (N.NNN s) [3] The following test left cyclic garbage behind: test_test_holds_cycle (gc-after-test.GcAfterTestTests...) Cycle 1 * ... test_traceback_cycle (gc-after-test.GcAfterTestTests...) (N.NNN s) [5] The following test left cyclic garbage behind: test_traceback_cycle (gc-after-test.GcAfterTestTests...) Cycle 1 * ... Ran 7 tests with 1 failures, 1 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Tests with errors: test_exception (gc-after-test.GcAfterTestTests...) Tests with failures: test_failure (gc-after-test.GcAfterTestTests...) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-gc.rst0000644000076600000240000001013315011314374025002 0ustar00m.howitzstaff============================ Garbage Collection Control ============================ When having problems that seem to be caused by memory-management errors, it can be helpful to adjust Python's cyclic garbage collector or to get garbage colection statistics. The ``--gc`` option can be used for this purpose. If you think you are getting a test failure due to a garbage collection problem, you can try disabling garbage collection by using the ``--gc`` option with a value of zero. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = ['--path', directory_with_tests] >>> from zope import testrunner >>> sys.argv = 'test --tests-pattern ^gc0$ --gc 0 -vv'.split() >>> _ = testrunner.run_internal(defaults) Cyclic garbage collection is disabled. Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: make_sure_gc_is_disabled (gc0) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Alternatively, if you think you are having a garbage collection related problem, you can cause garbage collection to happen more often by providing a low threshold: >>> sys.argv = 'test --tests-pattern ^gc1$ --gc 1 -vv'.split() >>> _ = testrunner.run_internal(defaults) Cyclic garbage collection threshold set to: (1,) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: make_sure_gc_threshold_is_one (gc1) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. You can specify up to 3 ``--gc`` options to set each of the 3 gc threshold values: >>> sys.argv = ('test --tests-pattern ^gcset$ --gc 701 --gc 11 --gc 9 -vv' ... .split()) >>> _ = testrunner.run_internal(defaults) Cyclic garbage collection threshold set to: (701, 11, 9) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: make_sure_gc_threshold_is_701_11_9 (gcset) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Specifying more than 3 ``--gc`` options is not allowed: >>> from io import StringIO >>> out = StringIO() >>> stdout = sys.stdout >>> sys.stdout = out >>> sys.argv = ('test --tests-pattern ^gcset$ --gc 701 --gc 42 --gc 11 --gc 9 -vv' ... .split()) >>> _ = testrunner.run_internal(defaults) Traceback (most recent call last): ... SystemExit: 1 >>> sys.stdout = stdout >>> print(out.getvalue()) Too many --gc options Garbage Collection Statistics ============================= You can enable gc debugging statistics using the ``--gc-options`` (``-G``) option. You should provide names of one or more of the flags described in the library documentation for the gc module. The output statistics are written to standard error. >>> from io import StringIO >>> err = StringIO() >>> stderr = sys.stderr >>> sys.stderr = err >>> sys.argv = ('test --tests-pattern ^gcstats$ -G DEBUG_STATS' ... ' -G DEBUG_COLLECTABLE -vv' ... .split()) >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: generate_some_gc_statistics (gcstats) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. >>> sys.stderr = stderr >>> print(err.getvalue()) # doctest: +ELLIPSIS gc: collect... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-knit.rst0000644000076600000240000001175315011314374025367 0ustar00m.howitzstaffKnitting in extra package directories ===================================== Python packages have __path__ variables that can be manipulated to add extra directories cntaining software used in the packages. The testrunner needs to be given extra information about this sort of situation. Let's look at an example. The testrunner-ex-knit-lib directory is a directory that we want to add to the Python path, but that we don't want to search for tests. It has a sample4 package and a products subpackage. The products subpackage adds the testrunner-ex-knit-products to it's __path__. We want to run tests from the testrunner-ex-knit-products directory. When we import these tests, we need to import them from the sample4.products package. We can't use the --path option to name testrunner-ex-knit-products. It isn't enough to add the containing directory to the test path because then we wouldn't be able to determine the package name properly. We might be able to use the --package option to run the tests from the sample4/products package, but we want to run tests in testrunner-ex that aren't in this package. We can use the --package-path option in this case. The --package-path option is like the --test-path option in that it defines a path to be searched for tests without affecting the python path. It differs in that it supplied a package name that is added a profex when importing any modules found. The --package-path option takes *two* arguments, a package name and file path. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> sys.path.append(os.path.join(this_directory, 'testrunner-ex-pp-lib')) >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... '--package-path', ... os.path.join(this_directory, 'testrunner-ex-pp-products'), ... 'sample4.products', ... ] >>> from zope import testrunner >>> old_argv = sys.argv >>> sys.argv = 'test --layer Layer111 -vv'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in 0.000 seconds. Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer111 in 0.000 seconds. Running: test_x1 (sample1.sampletests.test111.TestA...) test_y0 (sample1.sampletests.test111.TestA...) ... test_y0 (sampletests.test111) test_z1 (sampletests.test111) testrunner-ex/sampletests/../sampletestsl.rst test_extra_test_in_products (sample4.products.sampletests.Test...) test_another_test_in_products (sample4.products.more.sampletests.Test...) Ran 28 tests with 0 failures, 0 errors and 0 skipped in 0.008 seconds. Tearing down left over layers: Tear down samplelayers.Layer111 in 0.000 seconds. Tear down samplelayers.Layerx in 0.000 seconds. Tear down samplelayers.Layer11 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. In the example, the last test, test_extra_test_in_products, came from the products directory. As usual, we can select the knit-in packages or individual packages within knit-in packages: >>> sys.argv = 'test --package sample4.products -vv'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in 0.000 seconds. Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer111 in 0.000 seconds. Running: test_extra_test_in_products (sample4.products.sampletests.Test...) test_another_test_in_products (sample4.products.more.sampletests.Test...) Ran 2 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Tearing down left over layers: Tear down samplelayers.Layer111 in 0.000 seconds. Tear down samplelayers.Layerx in 0.000 seconds. Tear down samplelayers.Layer11 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. >>> sys.argv = 'test --package sample4.products.more -vv'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in 0.000 seconds. Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer111 in 0.000 seconds. Running: test_another_test_in_products (sample4.products.more.sampletests.Test...) Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Tearing down left over layers: Tear down samplelayers.Layer111 in 0.000 seconds. Tear down samplelayers.Layerx in 0.000 seconds. Tear down samplelayers.Layer11 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. Restore the arguments:: >>> sys.argv = old_argv ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-api.rst0000644000076600000240000002137015011314374026464 0ustar00m.howitzstaff============================================ Layers To Organize and Share Test Fixtures ============================================ A *layer* is an object providing setup and teardown methods used to setup and teardown the environment provided by the layer. It may also provide setup and teardown methods used to reset the environment provided by the layer between each test. Layers are generally implemented as classes using class methods. >>> class BaseLayer(object): ... @classmethod ... def setUp(cls): ... log('BaseLayer.setUp') ... ... @classmethod ... def tearDown(cls): ... log('BaseLayer.tearDown') ... ... @classmethod ... def testSetUp(cls): ... log('BaseLayer.testSetUp') ... ... @classmethod ... def testTearDown(cls): ... log('BaseLayer.testTearDown') Layers can extend other layers. .. important:: Layers do not explicitly invoke the setup and teardown methods of other layers -- the test runner does this for us in order to minimize the number of invocations. >>> class TopLayer(BaseLayer): ... @classmethod ... def setUp(cls): ... log('TopLayer.setUp') ... ... @classmethod ... def tearDown(cls): ... log('TopLayer.tearDown') ... ... @classmethod ... def testSetUp(cls): ... log('TopLayer.testSetUp') ... ... @classmethod ... def testTearDown(cls): ... log('TopLayer.testTearDown') ... Tests or test suites specify what layer they need by storing a reference in the ``layer`` attribute. >>> import unittest >>> class TestSpecifyingBaseLayer(unittest.TestCase): ... 'This TestCase explicitly specifies its layer' ... layer = BaseLayer ... name = 'TestSpecifyingBaseLayer' # For testing only ... ... def setUp(self): ... log('TestSpecifyingBaseLayer.setUp') ... ... def tearDown(self): ... log('TestSpecifyingBaseLayer.tearDown') ... ... def test1(self): ... log('TestSpecifyingBaseLayer.test1') ... ... def test2(self): ... log('TestSpecifyingBaseLayer.test2') ... >>> class TestSpecifyingNoLayer(unittest.TestCase): ... 'This TestCase specifies no layer' ... name = 'TestSpecifyingNoLayer' # For testing only ... def setUp(self): ... log('TestSpecifyingNoLayer.setUp') ... ... def tearDown(self): ... log('TestSpecifyingNoLayer.tearDown') ... ... def test1(self): ... log('TestSpecifyingNoLayer.test') ... ... def test2(self): ... log('TestSpecifyingNoLayer.test') ... Create a TestSuite containing two test suites, one for each of TestSpecifyingBaseLayer and TestSpecifyingNoLayer. >>> umbrella_suite = unittest.TestSuite() >>> umbrella_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestSpecifyingBaseLayer)) >>> no_layer_suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestSpecifyingNoLayer) >>> umbrella_suite.addTest(no_layer_suite) Before we can run the tests, we need to setup some helpers. >>> from zope.testrunner import options >>> from zope.testing.loggingsupport import InstalledHandler >>> import logging >>> log_handler = InstalledHandler('zope.testrunner.tests') >>> def log(msg): ... logging.getLogger('zope.testrunner.tests').info(msg) >>> def fresh_options(): ... opts = options.get_options(['--test-filter', '.*']) ... opts.resume_layer = None ... opts.resume_number = 0 ... return opts Now we run the tests. Note that the BaseLayer was not setup when the TestSpecifyingNoLayer was run and setup/torn down around the TestSpecifyingBaseLayer tests. >>> from zope.testrunner.runner import Runner >>> runner = Runner(options=fresh_options(), args=[], found_suites=[umbrella_suite]) >>> succeeded = runner.run() Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...BaseLayer tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up ...BaseLayer in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...BaseLayer in N.NNN seconds. Total: 4 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. Now lets specify a layer in the suite containing TestSpecifyingNoLayer and run the tests again. This demonstrates the other method of specifying a layer. This is generally how you specify what layer doctests need. >>> no_layer_suite.layer = BaseLayer >>> runner = Runner(options=fresh_options(), args=[], found_suites=[umbrella_suite]) >>> succeeded = runner.run() Running ...BaseLayer tests: Set up ...BaseLayer in N.NNN seconds. Ran 4 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...BaseLayer in N.NNN seconds. Clear our logged output, as we want to inspect it shortly. >>> log_handler.clear() Now lets also specify a layer in the TestSpecifyingNoLayer class and rerun the tests. This demonstrates that the most specific layer is used. It also shows the behavior of nested layers - because TopLayer extends BaseLayer, both the BaseLayer and TopLayer environments are setup when the TestSpecifyingNoLayer tests are run. >>> TestSpecifyingNoLayer.layer = TopLayer >>> runner = Runner(options=fresh_options(), args=[], found_suites=[umbrella_suite]) >>> succeeded = runner.run() Running ...BaseLayer tests: Set up ...BaseLayer in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...TopLayer tests: Set up ...TopLayer in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...TopLayer in N.NNN seconds. Tear down ...BaseLayer in N.NNN seconds. Total: 4 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. If we inspect our trace of what methods got called in what order, we can see that the layer setup and teardown methods only got called once. We can also see that the layer's test setup and teardown methods got called for each test using that layer in the right order. >>> def report(): ... print("Report:") ... for record in log_handler.records: ... print(record.getMessage()) >>> report() Report: BaseLayer.setUp BaseLayer.testSetUp TestSpecifyingBaseLayer.setUp TestSpecifyingBaseLayer.test1 TestSpecifyingBaseLayer.tearDown BaseLayer.testTearDown BaseLayer.testSetUp TestSpecifyingBaseLayer.setUp TestSpecifyingBaseLayer.test2 TestSpecifyingBaseLayer.tearDown BaseLayer.testTearDown TopLayer.setUp BaseLayer.testSetUp TopLayer.testSetUp TestSpecifyingNoLayer.setUp TestSpecifyingNoLayer.test TestSpecifyingNoLayer.tearDown TopLayer.testTearDown BaseLayer.testTearDown BaseLayer.testSetUp TopLayer.testSetUp TestSpecifyingNoLayer.setUp TestSpecifyingNoLayer.test TestSpecifyingNoLayer.tearDown TopLayer.testTearDown BaseLayer.testTearDown TopLayer.tearDown BaseLayer.tearDown Now lets stack a few more layers to ensure that our setUp and tearDown methods are called in the correct order. >>> from zope.testrunner.find import name_from_layer >>> class A(object): ... @classmethod ... def setUp(cls): ... log('%s.setUp' % name_from_layer(cls)) ... ... @classmethod ... def tearDown(cls): ... log('%s.tearDown' % name_from_layer(cls)) ... ... @classmethod ... def testSetUp(cls): ... log('%s.testSetUp' % name_from_layer(cls)) ... ... @classmethod ... def testTearDown(cls): ... log('%s.testTearDown' % name_from_layer(cls)) ... >>> class B(A): pass >>> class C(B): pass >>> class D(A): pass >>> class E(D): pass >>> class F(C,E): pass >>> class DeepTest(unittest.TestCase): ... layer = F ... def test(self): ... pass >>> suite = unittest.defaultTestLoader.loadTestsFromTestCase(DeepTest) >>> log_handler.clear() >>> runner = Runner(options=fresh_options(), args=[], found_suites=[suite]) >>> succeeded = runner.run() #doctest: +ELLIPSIS Running ...F tests: Set up ...A in N.NNN seconds. Set up ...B in N.NNN seconds. Set up ...C in N.NNN seconds. Set up ...D in N.NNN seconds. Set up ...E in N.NNN seconds. Set up ...F in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...F in N.NNN seconds. Tear down ...E in N.NNN seconds. Tear down ...D in N.NNN seconds. Tear down ...C in N.NNN seconds. Tear down ...B in N.NNN seconds. Tear down ...A in N.NNN seconds. >>> report() #doctest: +ELLIPSIS Report: ...A.setUp ...B.setUp ...C.setUp ...D.setUp ...E.setUp ...F.setUp ...A.testSetUp ...B.testSetUp ...C.testSetUp ...D.testSetUp ...E.testSetUp ...F.testSetUp ...F.testTearDown ...E.testTearDown ...D.testTearDown ...C.testTearDown ...B.testTearDown ...A.testTearDown ...F.tearDown ...E.tearDown ...D.tearDown ...C.tearDown ...B.tearDown ...A.tearDown ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-buff.rst0000644000076600000240000001736515011314374026646 0ustar00m.howitzstaffThis is a test for a fix in buffering of output from a layer in a subprocess. Prior to the change that this tests, output from within a test layer in a subprocess would be buffered. This could wreak havoc on supervising processes (or human) that would kill a test run if no output was seen for some period of time. First, we wrap stdout with an object that instruments it. It notes the time at which a given line was written. >>> import os, sys, datetime >>> class RecordingStreamWrapper(object): ... def __init__(self, wrapped): ... self.record = [] ... self.wrapped = wrapped ... @property ... def buffer(self): ... # runner._get_output_buffer attempts to write b'' ... # to us, and when that fails (see write()), accesses our .buffer directly. ... # That object deals in bytes. ... wrapper = self ... class buffer(object): ... def write(self, data): ... assert isinstance(data, bytes) ... wrapper.write(data.decode('utf-8')) ... def writelines(self, lines): ... for line in lines: ... self.write(line) ... def flush(self): ... wrapper.flush() ... return buffer() ... def write(self, out): ... # sys.stdout deals with native strings; ... # and raises TypeError for other things. We must do ... # the same. ... if not isinstance(out, str): ... raise TypeError ... self.record.append((out, datetime.datetime.now())) ... self.wrapped.write(out) ... def writelines(self, lines): ... for line in lines: ... self.write(line) ... def flush(self): ... self.wrapped.flush() ... >>> sys.stdout = RecordingStreamWrapper(sys.stdout) Now we actually call our test. If you open the file to which we are referring here (zope/testrunner/tests/testrunner-ex/sampletests_buffering.py) you will see two test suites, each with its own layer that does not know how to tear down. This forces the second suite to be run in a subprocess. That second suite has two tests. Both sleep for half a second each. >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... ] >>> argv = [sys.argv[0], ... '-vv', '--tests-pattern', '^sampletests_buffering.*'] >>> try: ... testrunner.run_internal(defaults, argv) ... record = sys.stdout.record ... finally: ... sys.stdout = sys.stdout.wrapped ... Running tests at level 1 Running sampletests_buffering.Layer1 tests: Set up sampletests_buffering.Layer1 in N.NNN seconds. Running: test_something (sampletests_buffering.TestSomething1...) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sampletests_buffering.Layer2 tests: Tear down sampletests_buffering.Layer1 ... not supported Running in a subprocess. Set up sampletests_buffering.Layer2 in N.NNN seconds. Running: test_something (sampletests_buffering.TestSomething2...) test_something2 (sampletests_buffering.TestSomething2...) Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down sampletests_buffering.Layer2 ... not supported Total: 3 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False Now we actually check the results we care about. We should see that there are two pauses of about half a second, one around the first test and one around the second. Before the change that this test verifies, there was a single pause of more than a second after the second suite ran. >>> def assert_progressive_output(): ... pause = datetime.timedelta(seconds=0.3) ... last_line, last_time = record.pop(0) ... print('---') ... for line, time in record: ... if time-last_time >= pause: ... # We paused! ... print('PAUSE FOUND BETWEEN THESE LINES:') ... print(''.join([last_line, line, '-' * 70])) ... last_line, last_time = line, time >>> assert_progressive_output() # doctest: +ELLIPSIS ---... PAUSE FOUND BETWEEN THESE LINES:... Running: test_something (sampletests_buffering.TestSomething2...) ---------------------------------------------------------------------- PAUSE FOUND BETWEEN THESE LINES: test_something (sampletests_buffering.TestSomething2...) test_something2 (sampletests_buffering.TestSomething2...) ---... Because this is a test based on timing, it may be somewhat fragile. However, on a relatively slow machine, this timing works out fine; I'm hopeful that this test will not be a source of spurious errors. If it is, we will have to readdress. Now let's do the same thing, but with multiple processes at once. We'll get a different result that has similar characteristics. Note that we don't have to use a special layer that doesn't support teardown to force the layer we're interested in to run in a subprocess: the test runner now does that when you ask for parallel execution. The other layer now just makes the test output non-deterministic, so we'll skip it. >>> sys.stdout = RecordingStreamWrapper(sys.stdout) >>> argv.extend(['-j', '2', '--layer=sampletests_buffering.Layer2']) >>> try: ... testrunner.run_internal(defaults, argv) ... record = sys.stdout.record ... finally: ... sys.stdout = sys.stdout.wrapped ... Running tests at level 1 Running .EmptyLayer tests: Set up .EmptyLayer in N.NNN seconds. Running: Ran 0 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. [Parallel tests running in sampletests_buffering.Layer2: .. LAYER FINISHED] Running sampletests_buffering.Layer2 tests: Running in a subprocess. Set up sampletests_buffering.Layer2 in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down sampletests_buffering.Layer2 ... not supported Tearing down left over layers: Tear down .EmptyLayer in N.NNN seconds. Total: 2 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False Notice that, with a -vv (or greater) verbosity, the parallel test run includes a progress report to keep track of tests run in the various layers. Because the actual results are saved to be displayed assembled in the original test order, the progress report shows up before we are given the news that the testrunner is starting Layer2. This is counterintuitive, but lets us keep the primary reporting information for the given layer in one location, while also giving us progress reports that can be used for keepalive analysis by a human or automated agent. In particular for the second point, notice below that, as before, the progress output is not buffered. >>> def assert_progressive_output(): ... pause = datetime.timedelta(seconds=0.3) ... last_line, last_time = record.pop(0) ... print('---') ... for line, time in record: ... if time-last_time >= pause: ... # We paused! ... print('PAUSE FOUND BETWEEN THIS OUTPUT:') ... print('\n'.join([last_line, line, '-'*70])) ... last_line, last_time = line, time >>> assert_progressive_output() # doctest: +ELLIPSIS ---... PAUSE FOUND BETWEEN THIS OUTPUT:... . . ---------------------------------------------------------------------- PAUSE FOUND BETWEEN THIS OUTPUT: . LAYER FINISHED ---... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-cantfind.rst0000644000076600000240000000332115011314374027475 0ustar00m.howitzstaffFailure to find layers should not pass silently =============================================== This is a regression test for the following bug: you try to run several test layers using subprocesses (e.g. because you used bin/test -j99), and the child process somehow is unable to find the layer it was supposed to be running. This is a serious problem that should not pass silently. Instead of setting up the conditions for this problem to actually occur in practice we'll simulate the subprocess invocation using --resume-layer. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] The test runner does some funky stuff in this case, specifically, it closes sys.stdout, which makes doctest unhappy, so we stub the close method out. >>> sys.stdout.close = lambda: None >>> from io import StringIO >>> orig_stderr = sys.stderr >>> sys.stderr = fake_stderr = StringIO() >>> sys.argv = 'test --resume-layer NoSuchLayer 0'.split() >>> from zope import testrunner >>> testrunner.run_internal(defaults) ********************************************************************** Cannot find layer NoSuchLayer ********************************************************************** Total: 0 tests, 0 failures, 1 errors and 0 skipped in 0.000 seconds. True It also prints to stderr to communicate with the parent process >>> print(fake_stderr.getvalue(), end='') 0 0 1 subprocess failed for NoSuchLayer Cleanup >>> del sys.stdout.close >>> sys.stderr = orig_stderr ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-cwd.rst0000644000076600000240000000261115011314374026465 0ustar00m.howitzstaffRegression test for https://github.com/zopefoundation/zope.testrunner/issues/6: when the test suite changes the current working directory, subprocess invocation might fail. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex-6') >>> defaults = [ ... '--path', os.path.relpath(directory_with_tests), ... '--tests-pattern', '^cwdtests?$', ... ] >>> orig_cwd = os.getcwd() >>> sys.argv = [os.path.relpath(testrunner_script), '-j2'] >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running .EmptyLayer tests: Set up .EmptyLayer in N.NNN seconds. Ran 0 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running cwdtests.Layer1 tests: Running in a subprocess. Set up cwdtests.Layer1 in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Tear down cwdtests.Layer1 in 0.000 seconds. Running cwdtests.Layer2 tests: Running in a subprocess. Set up cwdtests.Layer2 in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Tear down cwdtests.Layer2 in 0.000 seconds. Tearing down left over layers: Tear down .EmptyLayer in N.NNN seconds. Total: 2 tests, 0 failures, 0 errors and 0 skipped in 0.162 seconds. False >>> os.chdir(orig_cwd) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-instances.rst0000644000076600000240000001441115011314374027700 0ustar00m.howitzstaff========================================= Layers Implemented via Object Instances ========================================= Layers are generally implemented as classes using class methods, but regular objects can be used as well. They need to provide ``__module__``, ``__name__``, and ``__bases__`` attributes. >>> class TestLayer(object): ... def __init__(self, name, *bases): ... self.__name__ = name ... self.__bases__ = bases ... ... def setUp(self): ... log('%s.setUp' % self.__name__) ... ... def tearDown(self): ... log('%s.tearDown' % self.__name__) ... ... def testSetUp(self): ... log('%s.testSetUp' % self.__name__) ... ... def testTearDown(self): ... log('%s.testTearDown' % self.__name__) >>> BaseLayer = TestLayer('BaseLayer') >>> TopLayer = TestLayer('TopLayer', BaseLayer) Tests or test suites specify what layer they need by storing a reference in the ``layer`` attribute. >>> import unittest >>> class TestSpecifyingBaseLayer(unittest.TestCase): ... 'This TestCase explicitly specifies its layer' ... layer = BaseLayer ... name = 'TestSpecifyingBaseLayer' # For testing only ... ... def setUp(self): ... log('TestSpecifyingBaseLayer.setUp') ... ... def tearDown(self): ... log('TestSpecifyingBaseLayer.tearDown') ... ... def test1(self): ... log('TestSpecifyingBaseLayer.test1') ... ... def test2(self): ... log('TestSpecifyingBaseLayer.test2') ... >>> class TestSpecifyingNoLayer(unittest.TestCase): ... 'This TestCase specifies no layer' ... name = 'TestSpecifyingNoLayer' # For testing only ... def setUp(self): ... log('TestSpecifyingNoLayer.setUp') ... ... def tearDown(self): ... log('TestSpecifyingNoLayer.tearDown') ... ... def test1(self): ... log('TestSpecifyingNoLayer.test') ... ... def test2(self): ... log('TestSpecifyingNoLayer.test') ... Create a TestSuite containing two test suites, one for each of TestSpecifyingBaseLayer and TestSpecifyingNoLayer. >>> umbrella_suite = unittest.TestSuite() >>> umbrella_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestSpecifyingBaseLayer)) >>> no_layer_suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestSpecifyingNoLayer) >>> umbrella_suite.addTest(no_layer_suite) Before we can run the tests, we need to set up some helpers. >>> from zope.testrunner import options >>> from zope.testing.loggingsupport import InstalledHandler >>> import logging >>> log_handler = InstalledHandler('zope.testrunner.tests') >>> def log(msg): ... logging.getLogger('zope.testrunner.tests').info(msg) >>> def fresh_options(): ... opts = options.get_options(['--test-filter', '.*']) ... opts.resume_layer = None ... opts.resume_number = 0 ... return opts Now we run the tests. Note that the BaseLayer was not set up when the TestSpecifyingNoLayer was run and set up/torn down around the TestSpecifyingBaseLayer tests. >>> from zope.testrunner.runner import Runner >>> runner = Runner(options=fresh_options(), args=[], found_suites=[umbrella_suite]) >>> succeeded = runner.run() Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...BaseLayer tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up ...BaseLayer in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...BaseLayer in N.NNN seconds. Total: 4 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. Now lets specify a layer in the suite containing TestSpecifyingNoLayer and run the tests again. This demonstrates the other method of specifying a layer. This is generally how you specify what layer doctests need. >>> no_layer_suite.layer = BaseLayer >>> runner = Runner(options=fresh_options(), args=[], found_suites=[umbrella_suite]) >>> succeeded = runner.run() Running ...BaseLayer tests: Set up ...BaseLayer in N.NNN seconds. Ran 4 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...BaseLayer in N.NNN seconds. Clear our logged output, as we want to inspect it shortly. >>> log_handler.clear() Now lets also specify a layer in the TestSpecifyingNoLayer class and rerun the tests. This demonstrates that the most specific layer is used. It also shows the behavior of nested layers - because TopLayer extends BaseLayer, both the BaseLayer and TopLayer environments are set up when the TestSpecifyingNoLayer tests are run. >>> TestSpecifyingNoLayer.layer = TopLayer >>> runner = Runner(options=fresh_options(), args=[], found_suites=[umbrella_suite]) >>> succeeded = runner.run() Running ...BaseLayer tests: Set up ...BaseLayer in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...TopLayer tests: Set up ...TopLayer in N.NNN seconds. Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...TopLayer in N.NNN seconds. Tear down ...BaseLayer in N.NNN seconds. Total: 4 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. If we inspect our trace of what methods got called in what order, we can see that the layer setUp and tearDown methods only got called once. We can also see that the layer's test setUp and tearDown methods got called for each test using that layer in the right order. >>> def report(): ... print("Report:") ... for record in log_handler.records: ... print(record.getMessage()) >>> report() Report: BaseLayer.setUp BaseLayer.testSetUp TestSpecifyingBaseLayer.setUp TestSpecifyingBaseLayer.test1 TestSpecifyingBaseLayer.tearDown BaseLayer.testTearDown BaseLayer.testSetUp TestSpecifyingBaseLayer.setUp TestSpecifyingBaseLayer.test2 TestSpecifyingBaseLayer.tearDown BaseLayer.testTearDown TopLayer.setUp BaseLayer.testSetUp TopLayer.testSetUp TestSpecifyingNoLayer.setUp TestSpecifyingNoLayer.test TestSpecifyingNoLayer.tearDown TopLayer.testTearDown BaseLayer.testTearDown BaseLayer.testSetUp TopLayer.testSetUp TestSpecifyingNoLayer.setUp TestSpecifyingNoLayer.test TestSpecifyingNoLayer.tearDown TopLayer.testTearDown BaseLayer.testTearDown TopLayer.tearDown BaseLayer.tearDown ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-ntd.rst0000644000076600000240000003047115011314374026502 0ustar00m.howitzstaffLayers that can't be torn down ============================== A layer can have a tearDown method that raises NotImplementedError. If this is the case and there are no remaining tests to run, the test runner will just note that the tear down couldn't be done: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test -ssample2 --tests-pattern sampletests_ntd$'.split() >>> testrunner.run_internal(defaults) Running sample2.sampletests_ntd.Layer tests: Set up sample2.sampletests_ntd.Layer in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Tearing down left over layers: Tear down sample2.sampletests_ntd.Layer ... not supported False If the tearDown method raises NotImplementedError and there are remaining layers to run, the test runner will restart itself as a new process, resuming tests where it left off: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntd$'] >>> testrunner.run_internal(defaults) Running sample1.sampletests_ntd.Layer tests: Set up sample1.sampletests_ntd.Layer in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sample2.sampletests_ntd.Layer tests: Tear down sample1.sampletests_ntd.Layer ... not supported Running in a subprocess. Set up sample2.sampletests_ntd.Layer in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down sample2.sampletests_ntd.Layer ... not supported Running sample3.sampletests_ntd.Layer tests: Running in a subprocess. Set up sample3.sampletests_ntd.Layer in N.NNN seconds. Error in test test_error1 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error1 raise TypeError("Can we see errors") TypeError: Can we see errors Error in test test_error2 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error2 raise TypeError("I hope so") TypeError: I hope so Failure in test test_fail1 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail1 self.assertEqual(1, 2) AssertionError: 1 != 2 Failure in test test_fail2 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail2 self.assertEqual(1, 3) AssertionError: 1 != 3 Ran 6 tests with 2 failures, 2 errors and 0 skipped in N.NNN seconds. Tear down sample3.sampletests_ntd.Layer ... not supported Total: 8 tests, 2 failures, 2 errors and 0 skipped in N.NNN seconds. True in the example above, some of the tests run as a subprocess had errors and failures. They were displayed as usual and the failure and error statistice were updated as usual. Note that debugging doesn't work when running tests in a subprocess: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntd$', ... '-D', ] >>> testrunner.run_internal(defaults) Running sample1.sampletests_ntd.Layer tests: Set up sample1.sampletests_ntd.Layer in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sample2.sampletests_ntd.Layer tests: Tear down sample1.sampletests_ntd.Layer ... not supported Running in a subprocess. Set up sample2.sampletests_ntd.Layer in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down sample2.sampletests_ntd.Layer ... not supported Running sample3.sampletests_ntd.Layer tests: Running in a subprocess. Set up sample3.sampletests_ntd.Layer in N.NNN seconds. Error in test test_error1 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error1 raise TypeError("Can we see errors") TypeError: Can we see errors ********************************************************************** Can't post-mortem debug when running a layer as a subprocess! ********************************************************************** Error in test test_error2 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error2 raise TypeError("I hope so") TypeError: I hope so ********************************************************************** Can't post-mortem debug when running a layer as a subprocess! ********************************************************************** Error in test test_fail1 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail1 self.assertEqual(1, 2) AssertionError: 1 != 2 ********************************************************************** Can't post-mortem debug when running a layer as a subprocess! ********************************************************************** Error in test test_fail2 (sample3.sampletests_ntd.TestSomething...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail2 self.assertEqual(1, 3) AssertionError: 1 != 3 ********************************************************************** Can't post-mortem debug when running a layer as a subprocess! ********************************************************************** Ran 6 tests with 0 failures, 4 errors and 0 skipped in N.NNN seconds. Tear down sample3.sampletests_ntd.Layer ... not supported Total: 8 tests, 0 failures, 4 errors and 0 skipped in N.NNN seconds. True Similarly, pdb.set_trace doesn't work when running tests in a layer that is run as a subprocess: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntds'] >>> testrunner.run_internal(defaults) Running sample1.sampletests_ntds.Layer tests: Set up sample1.sampletests_ntds.Layer in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Running sample2.sampletests_ntds.Layer tests: Tear down sample1.sampletests_ntds.Layer ... not supported Running in a subprocess. Set up sample2.sampletests_ntds.Layer in 0.000 seconds. > testrunner-ex/sample2/sampletests_ntds.py(37)test_something()->None -> pdb.set_trace() # noqa: T100 pdb.set_trace found (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** > testrunner-ex/sample2/sampletests_ntds.py(40)test_something2()->None -> pdb.set_trace() # noqa: T100 pdb.set_trace found (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** > testrunner-ex/sample2/sampletests_ntds.py(43)test_something3()->None -> pdb.set_trace() # noqa: T100 pdb.set_trace found (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** > testrunner-ex/sample2/sampletests_ntds.py(46)test_something4()->None -> pdb.set_trace() # noqa: T100 pdb.set_trace found (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** > testrunner-ex/sample2/sampletests_ntds.py(52)f()->None -> pdb.set_trace() # noqa: T100 pdb.set_trace found (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** > (3)?() -> import pdb; pdb.set_trace() (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** > testrunner-ex/sample2/sampletests_ntds.py(NNN)f() -> pdb.set_trace() # noqa: T100 pdb.set_trace found (Pdb) c ********************************************************************** Can't use pdb.set_trace when running a layer as a subprocess! ********************************************************************** Ran 7 tests with 0 failures, 0 errors and 0 skipped in 0.008 seconds. Tear down sample2.sampletests_ntds.Layer ... not supported Total: 8 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False If you want to use pdb from a test in a layer that is run as a subprocess, then rerun the test runner selecting *just* that layer so that it's not run as a subprocess. If a test is run in a subprocess and it generates output on stderr (as stderrtest does), the output is ignored (but it doesn't cause a SubprocessError like it once did). >>> from io import StringIO >>> real_stderr = sys.stderr >>> sys.stderr = StringIO() >>> sys.argv = [testrunner_script, '-s', 'sample2', '--tests-pattern', ... '(sampletests_ntd$|stderrtest)'] >>> testrunner.run_internal(defaults) Running sample2.sampletests_ntd.Layer tests: Set up sample2.sampletests_ntd.Layer in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Running sample2.stderrtest.Layer tests: Tear down sample2.sampletests_ntd.Layer ... not supported Running in a subprocess. Set up sample2.stderrtest.Layer in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.002 seconds. Tear down sample2.stderrtest.Layer in 0.000 seconds. Total: 2 tests, 0 failures, 0 errors and 0 skipped in 0.197 seconds. False >>> print((sys.stderr.getvalue())) A message on stderr. Please ignore (expected in test output). >>> sys.stderr = real_stderr When a layer is run in a subprocess, the test IDs of any failures and errors it generates are passed to the parent process via the child's stderr. The parent reads these IDs in parallel with reading other output from the child, so this works even if there are enough failures to overflow the capacity of the stderr pipe. >>> argv = [testrunner_script, '--tests-pattern', '^sampletests_many$'] >>> testrunner.run_internal(defaults, argv) Running sampletests_many.Layer1 tests: Set up sampletests_many.Layer1 in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sampletests_many.Layer2 tests: Tear down sampletests_many.Layer1 ... not supported Running in a subprocess. Set up sampletests_many.Layer2 in N.NNN seconds. Failure in test test_some_very_long_test_name_with_padding_000 (sampletests_many.TestMany...) ... Ran 1000 tests with 1000 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down sampletests_many.Layer2 in N.NNN seconds. Total: 1001 tests, 1000 failures, 0 errors and 0 skipped in N.NNN seconds. True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers-topological-sort.rst0000644000076600000240000000632115011314374031213 0ustar00m.howitzstaffNow lets stack a few more layers to ensure that our setUp and tearDown methods are called in the correct order. >>> from zope.testing.loggingsupport import InstalledHandler >>> from zope.testrunner.find import name_from_layer >>> from zope.testrunner import options >>> from zope.testrunner.runner import Runner >>> import logging >>> import unittest >>> def fresh_options(): ... opts = options.get_options(['--test-filter', '.*']) ... opts.resume_layer = None ... opts.resume_number = 0 ... return opts >>> def log(msg): ... logging.getLogger('zope.testrunner.tests').info(msg) >>> log_handler = InstalledHandler('zope.testrunner.tests') >>> class A(object): ... def setUp(cls): ... log('%s.setUp' % name_from_layer(cls)) ... setUp = classmethod(setUp) ... ... def tearDown(cls): ... log('%s.tearDown' % name_from_layer(cls)) ... tearDown = classmethod(tearDown) ... ... def testSetUp(cls): ... log('%s.testSetUp' % name_from_layer(cls)) ... testSetUp = classmethod(testSetUp) ... ... def testTearDown(cls): ... log('%s.testTearDown' % name_from_layer(cls)) ... testTearDown = classmethod(testTearDown) ... >>> class AB(A): pass >>> class AC(A): pass >>> class AAAABD(AB): pass >>> class ZZZABE(AB): pass >>> class MMMACF(AC): pass >>> class DeepTest1(unittest.TestCase): ... layer = AAAABD ... def test(self): ... pass >>> class DeepTest2(unittest.TestCase): ... layer = MMMACF ... def test(self): ... pass >>> class DeepTest3(unittest.TestCase): ... layer = ZZZABE ... def test(self): ... pass >>> class QuickTests(unittest.TestCase): ... def test(self): ... pass >>> suite = unittest.TestSuite() >>> suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(DeepTest1)) >>> suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(DeepTest2)) >>> suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(DeepTest3)) >>> suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(QuickTests)) >>> log_handler.clear() >>> runner = Runner(options=fresh_options(), args=[], found_suites=[suite]) >>> succeeded = runner.run() #doctest: +ELLIPSIS Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...AAAABD tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up ...A in N.NNN seconds. Set up ...AB in N.NNN seconds. Set up ...AAAABD in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...ZZZABE tests: Tear down ...AAAABD in N.NNN seconds. Set up ...ZZZABE in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running ...MMMACF tests: Tear down ...ZZZABE in N.NNN seconds. Tear down ...AB in N.NNN seconds. Set up ...AC in N.NNN seconds. Set up ...MMMACF in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down ...MMMACF in N.NNN seconds. Tear down ...AC in N.NNN seconds. Tear down ...A in N.NNN seconds. Total: 4 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-layers.rst0000644000076600000240000002335015011314374025715 0ustar00m.howitzstaff========================== Selecting Tests By Layer ========================== We can select which layers to run using the --layer option: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test --layer 112 --layer Unit'.split() >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer112 tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer112 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 182 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False We can also specify that we want to run only the unit tests: >>> sys.argv = 'test -u'.split() >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Or that we want to run all of the tests except for the unit tests: >>> sys.argv = 'test -f'.split() >>> testrunner.run_internal(defaults) Running samplelayers.Layer1 tests: Set up samplelayers.Layer1 in N.NNN seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer11 tests: Set up samplelayers.Layer11 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer111 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer112 tests: Tear down samplelayers.Layer111 in N.NNN seconds. Set up samplelayers.Layer112 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer12 tests: Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer121 tests: Set up samplelayers.Layer121 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer122 tests: Tear down samplelayers.Layer121 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 165 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False Or we can explicitly say that we want both unit and non-unit tests. >>> sys.argv = 'test -uf'.split() >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer1 tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer11 tests: Set up samplelayers.Layer11 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer111 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer112 tests: Tear down samplelayers.Layer111 in N.NNN seconds. Set up samplelayers.Layer112 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer12 tests: Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer121 tests: Set up samplelayers.Layer121 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer122 tests: Tear down samplelayers.Layer121 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 321 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False It is possible to force the layers to run in subprocesses and parallelize them. ``EmptyLayer`` will be inserted as first to start spreading out subprocesses ASAP. >>> sys.argv = [testrunner_script, '-j2'] >>> testrunner.run_internal(defaults) Running .EmptyLayer tests: Set up .EmptyLayer in N.NNN seconds. Ran 0 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running zope.testrunner.layer.UnitTests tests: Running in a subprocess. Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Running samplelayers.Layer1 tests: Running in a subprocess. Set up samplelayers.Layer1 in N.NNN seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Running samplelayers.Layer11 tests: Running in a subprocess. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer11 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Running samplelayers.Layer111 tests: Running in a subprocess. Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer111 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer111 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Running samplelayers.Layer112 tests: Running in a subprocess. Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer112 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Running samplelayers.Layer12 tests: Running in a subprocess. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Running samplelayers.Layer121 tests: Running in a subprocess. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Set up samplelayers.Layer121 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer121 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Running samplelayers.Layer122 tests: Running in a subprocess. Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Tearing down left over layers: Tear down .EmptyLayer in N.NNN seconds. Total: 321 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-leaks-err.rst0000644000076600000240000000145315011314374026303 0ustar00m.howitzstaffDebugging Memory Leaks without a debug build of Python ====================================================== To use the --report-refcounts (-r) to detect or debug memory leaks, you must have a debug build of Python. Without a debug build, you will get an error message: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> sys.argv = 'test -r -N6'.split() >>> _ = testrunner.run_internal(defaults) The Python you are running was not configured with --with-pydebug. This is required to use the --report-refcounts option. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-leaks.rst0000644000076600000240000002511315011314374025514 0ustar00m.howitzstaffDebugging Memory Leaks ====================== The --report-refcounts (-r) option can be used with the --repeat (-N) option to detect and diagnose memory leaks. To use this option, you must configure Python with the --with-pydebug option. (On Unix, pass this option to configure and then build Python.) >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> sys.argv = 'test --layer Layer11$ --layer Layer12$ -N4 -r'.split() >>> _ = testrunner.run_internal(defaults) Running samplelayers.Layer11 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer11 in 0.000 seconds. Iteration 1 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.013 seconds. Iteration 2 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.012 seconds. sys refcount=100401 change=0 Iteration 3 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.012 seconds. sys refcount=100401 change=0 Iteration 4 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.013 seconds. sys refcount=100401 change=0 Running samplelayers.Layer12 tests: Tear down samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Iteration 1 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.013 seconds. Iteration 2 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.012 seconds. sys refcount=100411 change=0 Iteration 3 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.012 seconds. sys refcount=100411 change=0 Iteration 4 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.012 seconds. sys refcount=100411 change=0 Tearing down left over layers: Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. Total: 68 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. Each layer is repeated the requested number of times. For each iteration after the first, the system refcount and change in system refcount is shown. The system refcount is the total of all refcount in the system. When a refcount on any object is changed, the system refcount is changed by the same amount. Tests that don't leak show zero changes in systen refcount. Let's look at an example test that leaks: >>> sys.argv = 'test --tests-pattern leak -N4 -r'.split() >>> _ = testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests:... Iteration 1 Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Iteration 2 Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sys refcount=92506 change=12 Iteration 3 Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sys refcount=92513 change=12 Iteration 4 Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sys refcount=92520 change=12 Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Here we see that the system refcount is increating. If we specify a verbosity greater than one, we can get details broken out by object type (or class): >>> sys.argv = 'test --tests-pattern leak -N5 -r -v'.split() >>> _ = testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests:... Iteration 1 Running: . Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. Iteration 2 Running: . Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sum detail refcount=95832 sys refcount=105668 change=16 Leak details, changes in instances and refcounts by type/class: type/class insts refs ------------------------------------------------------- ----- ---- classobj 0 1 dict 2 2 float 1 1 int 2 2 leak.ClassicLeakable 1 1 leak.Leakable 1 1 str 0 4 tuple 1 1 type 0 3 ------------------------------------------------------- ----- ---- total 8 16 Iteration 3 Running: . Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sum detail refcount=95844 sys refcount=105680 change=12 Leak details, changes in instances and refcounts by type/class: type/class insts refs ------------------------------------------------------- ----- ---- classobj 0 1 dict 2 2 float 1 1 int -1 0 leak.ClassicLeakable 1 1 leak.Leakable 1 1 str 0 4 tuple 1 1 type 0 1 ------------------------------------------------------- ----- ---- total 5 12 Iteration 4 Running: . Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sum detail refcount=95856 sys refcount=105692 change=12 Leak details, changes in instances and refcounts by type/class: type/class insts refs ------------------------------------------------------- ----- ---- classobj 0 1 dict 2 2 float 1 1 leak.ClassicLeakable 1 1 leak.Leakable 1 1 str 0 4 tuple 1 1 type 0 1 ------------------------------------------------------- ----- ---- total 6 12 Iteration 5 Running: . Ran 1 tests with 0 failures, 0 errors and 0 skipped in 0.000 seconds. sum detail refcount=95868 sys refcount=105704 change=12 Leak details, changes in instances and refcounts by type/class: type/class insts refs ------------------------------------------------------- ----- ---- classobj 0 1 dict 2 2 float 1 1 leak.ClassicLeakable 1 1 leak.Leakable 1 1 str 0 4 tuple 1 1 type 0 1 ------------------------------------------------------- ----- ---- total 6 12 Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. It is instructive to analyze the results in some detail. The test being run was designed to intentionally leak: class ClassicLeakable: def __init__(self): self.x = 'x' class Leakable(object): def __init__(self): self.x = 'x' leaked = [] class TestSomething(unittest.TestCase): def testleak(self): leaked.append((ClassicLeakable(), Leakable(), time.time())) Let's go through this by type. float, leak.ClassicLeakable, leak.Leakable, and tuple We leak one of these every time. This is to be expected because we are adding one of these to the list every time. str We don't leak any instances, but we leak 4 references. These are due to the instance attributes avd values. dict We leak 2 of these, one for each ClassicLeakable and Leakable instance. classobj We increase the number of classobj instance references by one each time because each ClassicLeakable instance has a reference to its class. This instances increases the references in it's class, which increases the total number of references to classic classes (clasobj instances). type For most interations, we increase the number of type references by one for the same reason we increase the number of clasobj references by one. The increase of the number of type references by 3 in the second iteration is puzzling, but illustrates that this sort of data is often puzzling. int The change in the number of int instances and references in this example is a side effect of the statistics being gathered. Lots of integers are created to keep the memory statistics used here. The summary statistics include the sum of the detail refcounts. (Note that this sum is less than the system refcount. This is because the detailed analysis doesn't inspect every object. Not all objects in the system are returned by sys.getobjects.) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-nestedcode.rst0000644000076600000240000000523715011314374026537 0ustar00m.howitzstaffThis testrunner_ex subdirectory contains a number of sample packages with tests. Let's run the tests found here. We simulate here, that you use nested locations for your different source packages and that within your source, you checked out other source directories >>> import os.path >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', this_directory, ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> import sys >>> sys.argv = ['test'] >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer1 tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer11 tests: Set up samplelayers.Layer11 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer111 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer112 tests: Tear down samplelayers.Layer111 in N.NNN seconds. Set up samplelayers.Layer112 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer12 tests: Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer121 tests: Set up samplelayers.Layer121 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer122 tests: Tear down samplelayers.Layer121 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 321 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-new-threads.rst0000644000076600000240000000313415011314374026635 0ustar00m.howitzstaff=================== Threads Reporting =================== For each test zope.testrunner checks if new threads are left behind. >>> import time >>> import os >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = ['--path', directory_with_tests] >>> from zope import testrunner >>> args = ['test', '--tests-pattern', '^new_threads$'] >>> _ = testrunner.run_internal(defaults, args) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. The following test left new threads behind: test_leave_thread_behind (new_threads.TestNewThreadsReporting...) New thread(s): [] Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. >>> time.sleep(1) It is possible to ignore this reporting for known threads. >>> import time >>> import os >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = ['--path', directory_with_tests] >>> from zope import testrunner >>> args = ['test', '--tests-pattern', '^new_threads$', '--ignore-new-thread=t1'] >>> _ = testrunner.run_internal(defaults, args) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. >>> time.sleep(1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-profiling-cprofiler.rst0000644000076600000240000000267115011314374030375 0ustar00m.howitzstaffProfiling ========= The testrunner includes the ability to profile the test execution with cProfile via the `--profile=cProfile` option:: >>> import os, sys, tempfile >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> sys.path.append(directory_with_tests) >>> tempdir = tempfile.mkdtemp(prefix='zope.testrunner-') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... '--profile-directory', tempdir, ... ] >>> sys.argv = [testrunner_script, '--profile=cProfile'] When the tests are run, we get profiling output:: >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running... ... ncalls tottime percall cumtime percall filename:lineno(function)... ... Profiling also works across layers:: >>> sys.argv = [testrunner_script, '-ssample2', '--profile=cProfile', ... '--tests-pattern', 'sampletests_ntd'] >>> testrunner.run_internal(defaults) Running... Tear down ... not supported... ncalls tottime percall cumtime percall filename:lineno(function)... The testrunner creates temnporary files containing cProfiler profiler data:: >>> os.listdir(tempdir) ['tests_profile.cZj2jt.prof', 'tests_profile.yHD-so.prof'] It deletes these when rerun. We'll delete these ourselves:: >>> import shutil >>> shutil.rmtree(tempdir) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-profiling.rst0000644000076600000240000000354715011314374026415 0ustar00m.howitzstaffProfiling ========= The testrunner supports hotshot and cProfile profilers. Hotshot profiler support does not work with python2.6 >>> import os.path, sys, tempfile >>> profiler = '--profile=hotshot' >>> if sys.hexversion >= 0x02060000: ... profiler = '--profile=cProfile' The testrunner includes the ability to profile the test execution with hotshot via the --profile option, if it a python <= 2.6 >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> sys.path.append(directory_with_tests) >>> tempdir = tempfile.mkdtemp(prefix='zope.testrunner-') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... '--profile-directory', tempdir, ... ] >>> sys.argv = [testrunner_script, profiler] When the tests are run, we get profiling output. >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: ... Running samplelayers.Layer1 tests: ... Running samplelayers.Layer11 tests: ... ncalls tottime percall cumtime percall filename:lineno(function) ... Total: ... tests, 0 failures, 0 errors and 0 skipped in ... seconds. False Profiling also works across layers. >>> sys.argv = [testrunner_script, '-ssample2', profiler, ... '--tests-pattern', 'sampletests_ntd'] >>> testrunner.run_internal(defaults) Running... Tear down ... not supported... ncalls tottime percall cumtime percall filename:lineno(function)... The testrunner creates temnporary files containing hotshot profiler data: >>> os.listdir(tempdir) ['tests_profile.cZj2jt.prof', 'tests_profile.yHD-so.prof'] It deletes these when rerun. We'll delete these ourselves: >>> import shutil >>> shutil.rmtree(tempdir) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-progress.rst0000644000076600000240000005167415011314374026274 0ustar00m.howitzstaffTest Progress ============= If the --progress (-p) option is used, progress information is printed and a carriage return (rather than a new-line) is printed between detail lines. Let's look at the effect of --progress (-p) at different levels of verbosity. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test --layer 122 -p'.split() >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Running: 1/26 (3.8%)##r## ##r## 2/26 (7.7%)##r## ##r## 3/26 (11.5%)##r## ##r## 4/26 (15.4%)##r## ##r## 5/26 (19.2%)##r## ##r## 6/26 (23.1%)##r## ##r## 7/26 (26.9%)##r## ##r## 8/26 (30.8%)##r## ##r## 9/26 (34.6%)##r## ##r## 10/26 (38.5%)##r## ##r## 11/26 (42.3%)##r## ##r## 12/26 (46.2%)##r## ##r## 13/26 (50.0%)##r## ##r## 14/26 (53.8%)##r## ##r## 15/26 (57.7%)##r## ##r## 16/26 (61.5%)##r## ##r## 17/26 (65.4%)##r## ##r## 18/26 (69.2%)##r## ##r## 19/26 (73.1%)##r## ##r## 20/26 (76.9%)##r## ##r## 21/26 (80.8%)##r## ##r## 22/26 (84.6%)##r## ##r## 23/26 (88.5%)##r## ##r## 24/26 (92.3%)##r## ##r## 25/26 (96.2%)##r## ##r## 26/26 (100.0%)##r## ##r## Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. False (Note that, in the examples above and below, we show "##r##" followed by new lines where carriage returns would appear in actual output.) Using a single level of verbosity causes test descriptions to be output, but only if they fit in the terminal width. The default width, when the terminal width can't be determined, is 80: >>> sys.argv = 'test --layer 122 -pv'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Running: 1/26 (3.8%) test_x1 (sample1.sampletests.test122.TestA...)##r## ##r## 2/26 (7.7%) test_y0 (sample1.sampletests.test122.TestA...)##r## ##r## 3/26 (11.5%) test_z0 (sample1.sampletests.test122.TestA...)##r## ##r## 4/26 (15.4%) test_x0 (sample1.sampletests.test122.TestB...)##r## ##r## 5/26 (19.2%) test_y1 (sample1.sampletests.test122.TestB...)##r## ##r## 6/26 (23.1%) test_z0 (sample1.sampletests.test122.TestB...)##r## ##r## 7/26 (26.9%) test_1 (sample1.sampletests.test122.TestNotMuch...)##r## ##r## 8/26 (30.8%) test_2 (sample1.sampletests.test122.TestNotMuch...)##r## ##r## 9/26 (34.6%) test_3 (sample1.sampletests.test122.TestNotMuch...)##r## ##r## 10/26 (38.5%) test_x0 (sample1.sampletests.test122)##r## ##r## 11/26 (42.3%) test_y0 (sample1.sampletests.test122)##r## ##r## 12/26 (46.2%) test_z1 (sample1.sampletests.test122)##r## ##r## testrunner-ex/sample1/sampletests/../../sampletestsl.rst##r## ##r## 14/26 (53.8%) test_x1 (sampletests.test122.TestA...)##r## ##r## 15/26 (57.7%) test_y0 (sampletests.test122.TestA...)##r## ##r## 16/26 (61.5%) test_z0 (sampletests.test122.TestA...)##r## ##r## 17/26 (65.4%) test_x0 (sampletests.test122.TestB...)##r## ##r## 18/26 (69.2%) test_y1 (sampletests.test122.TestB...)##r## ##r## 19/26 (73.1%) test_z0 (sampletests.test122.TestB...)##r## ##r## 20/26 (76.9%) test_1 (sampletests.test122.TestNotMuch...)##r## ##r## 21/26 (80.8%) test_2 (sampletests.test122.TestNotMuch...)##r## ##r## 22/26 (84.6%) test_3 (sampletests.test122.TestNotMuch...)##r## ##r## 23/26 (88.5%) test_x0 (sampletests.test122)##r## ##r## 24/26 (92.3%) test_y0 (sampletests.test122)##r## ##r## 25/26 (96.2%) test_z1 (sampletests.test122)##r## ##r## testrunner-ex/sampletests/../sampletestsl.rst##r## ##r## Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. False The terminal width is determined using the curses module. To see that, we'll provide a fake curses module: >>> class FakeCurses: ... class error(Exception): ... pass ... def setupterm(self): ... pass ... def tigetnum(self, ignored): ... return 60 >>> old_curses = sys.modules.get('curses') >>> sys.modules['curses'] = FakeCurses() >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Running: 1/26 (3.8%) test_x1 (...pletests.test122.TestA...)##r## ##r## 2/26 (7.7%) test_y0 (...pletests.test122.TestA...)##r## ##r## 3/26 (11.5%) test_z0 (...letests.test122.TestA...)##r## ##r## 4/26 (15.4%) test_x0 (...letests.test122.TestB...)##r## ##r## 5/26 (19.2%) test_y1 (...letests.test122.TestB...)##r## ##r## 6/26 (23.1%) test_z0 (...letests.test122.TestB...)##r## ##r## 7/26 (26.9%) test_1 (...sts.test122.TestNotMuch...)##r## ##r## 8/26 (30.8%) test_2 (...sts.test122.TestNotMuch...)##r## ##r## 9/26 (34.6%) test_3 (...sts.test122.TestNotMuch...)##r## ##r## 10/26 (38.5%) test_x0 (sample1.sampletests.test122)##r## ##r## 11/26 (42.3%) test_y0 (sample1.sampletests.test122)##r## ##r## 12/26 (46.2%) test_z1 (sample1.sampletests.test122)##r## ##r## 13/26 (50.0%) ... e1/sampletests/../../sampletestsl.rst##r## ##r## 14/26 (53.8%) test_x1 (...etests.test122.TestA...)##r## ##r## 15/26 (57.7%) test_y0 (...etests.test122.TestA...)##r## ##r## 16/26 (61.5%) test_z0 (...etests.test122.TestA...)##r## ##r## 17/26 (65.4%) test_x0 (...etests.test122.TestB...)##r## ##r## 18/26 (69.2%) test_y1 (...etests.test122.TestB...)##r## ##r## 19/26 (73.1%) test_z0 (...etests.test122.TestB...)##r## ##r## 20/26 (76.9%) test_1 (...ts.test122.TestNotMuch...)##r## ##r## 21/26 (80.8%) test_2 (...ts.test122.TestNotMuch...)##r## ##r## 22/26 (84.6%) test_3 (...ts.test122.TestNotMuch...)##r## ##r## 23/26 (88.5%) test_x0 (sampletests.test122)##r## ##r## 24/26 (92.3%) test_y0 (sampletests.test122)##r## ##r## 25/26 (96.2%) test_z1 (sampletests.test122)##r## ##r## 26/26 (100.0%) ... r-ex/sampletests/../sampletestsl.rst##r## ##r## Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. False >>> sys.modules['curses'] = old_curses If a second or third level of verbosity are added, we get additional information. >>> sys.argv = 'test --layer 122 -pvv -t !rst'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: 1/24 (4.2%) test_x1 (sample1.sampletests.test122.TestA...)##r## ##r## 2/24 (8.3%) test_y0 (sample1.sampletests.test122.TestA...)##r## ##r## 3/24 (12.5%) test_z0 (sample1.sampletests.test122.TestA...)##r## ##r## 4/24 (16.7%) test_x0 (sample1.sampletests.test122.TestB...)##r## ##r## 5/24 (20.8%) test_y1 (sample1.sampletests.test122.TestB...)##r## ##r## 6/24 (25.0%) test_z0 (sample1.sampletests.test122.TestB...)##r## ##r## 7/24 (29.2%) test_1 (sample1.sampletests.test122.TestNotMuch...)##r## ##r## 8/24 (33.3%) test_2 (sample1.sampletests.test122.TestNotMuch...)##r## ##r## 9/24 (37.5%) test_3 (sample1.sampletests.test122.TestNotMuch...)##r## ##r## 10/24 (41.7%) test_x0 (sample1.sampletests.test122)##r## ##r## 11/24 (45.8%) test_y0 (sample1.sampletests.test122)##r## ##r## 12/24 (50.0%) test_z1 (sample1.sampletests.test122)##r## ##r## 13/24 (54.2%) test_x1 (sampletests.test122.TestA...)##r## ##r## 14/24 (58.3%) test_y0 (sampletests.test122.TestA...)##r## ##r## 15/24 (62.5%) test_z0 (sampletests.test122.TestA...)##r## ##r## 16/24 (66.7%) test_x0 (sampletests.test122.TestB...)##r## ##r## 17/24 (70.8%) test_y1 (sampletests.test122.TestB...)##r## ##r## 18/24 (75.0%) test_z0 (sampletests.test122.TestB...)##r## ##r## 19/24 (79.2%) test_1 (sampletests.test122.TestNotMuch...)##r## ##r## 20/24 (83.3%) test_2 (sampletests.test122.TestNotMuch...)##r## ##r## 21/24 (87.5%) test_3 (sampletests.test122.TestNotMuch...)##r## ##r## 22/24 (91.7%) test_x0 (sampletests.test122)##r## ##r## 23/24 (95.8%) test_y0 (sampletests.test122)##r## ##r## 24/24 (100.0%) test_z1 (sampletests.test122)##r## ##r## Ran 24 tests with 0 failures, 0 errors and 0 skipped in 0.006 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False Note that, in this example, we used a test-selection pattern starting with '!' to exclude tests containing the string "rst". >>> sys.argv = 'test --layer 122 -pvvv -t!(rst|NotMuch)'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: 1/18 (5.6%) test_x1 (sample1.sampletests.test122.TestA...) (0.000 s)##r## ##r## 2/18 (11.1%) test_y0 (sample1.sampletests.test122.TestA...) (0.000 s)##r## ##r## 3/18 (16.7%) test_z0 (sample1.sampletests.test122.TestA...) (0.000 s)##r## ##r## 4/18 (22.2%) test_x0 (sample1.sampletests.test122.TestB...) (0.000 s)##r## ##r## 5/18 (27.8%) test_y1 (sample1.sampletests.test122.TestB...) (0.000 s)##r## ##r## 6/18 (33.3%) test_z0 (sample1.sampletests.test122.TestB...) (0.000 s)##r## ##r## 7/18 (38.9%) test_x0 (sample1.sampletests.test122) (0.001 s)##r## ##r## 8/18 (44.4%) test_y0 (sample1.sampletests.test122) (0.001 s)##r## ##r## 9/18 (50.0%) test_z1 (sample1.sampletests.test122) (0.001 s)##r## ##r## 10/18 (55.6%) test_x1 (sampletests.test122.TestA...) (0.000 s)##r## ##r## 11/18 (61.1%) test_y0 (sampletests.test122.TestA...) (0.000 s)##r## ##r## 12/18 (66.7%) test_z0 (sampletests.test122.TestA...) (0.000 s)##r## ##r## 13/18 (72.2%) test_x0 (sampletests.test122.TestB...) (0.000 s)##r## ##r## 14/18 (77.8%) test_y1 (sampletests.test122.TestB...) (0.000 s)##r## ##r## 15/18 (83.3%) test_z0 (sampletests.test122.TestB...) (0.000 s)##r## ##r## 16/18 (88.9%) test_x0 (sampletests.test122) (0.001 s)##r## ##r## 17/18 (94.4%) test_y0 (sampletests.test122) (0.001 s)##r## ##r## 18/18 (100.0%) test_z1 (sampletests.test122) (0.001 s)##r## ##r## Ran 18 tests with 0 failures, 0 errors and 0 skipped in 0.006 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False In this example, we also excluded tests with "NotMuch" in their names. Unfortunately, the time data above doesn't buy us much because, in practice, the line is cleared before there is time to see the times. :/ Autodetecting progress ---------------------- The --auto-progress option will determine if stdout is a terminal, and only enable progress output if so. Let's pretend we have a terminal >>> class Terminal(object): ... def __init__(self, stream): ... self._stream = stream ... def __getattr__(self, attr): ... return getattr(self._stream, attr) ... def isatty(self): ... return True >>> real_stdout = sys.stdout >>> sys.stdout = Terminal(sys.stdout) >>> sys.argv = 'test -u -t test_one.TestNotMuch --auto-progress'.split() >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: 1/6 (16.7%)##r## ##r## 2/6 (33.3%)##r## ##r## 3/6 (50.0%)##r## ##r## 4/6 (66.7%)##r## ##r## 5/6 (83.3%)##r## ##r## 6/6 (100.0%)##r## ##r## Ran 6 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Let's stop pretending >>> sys.stdout = real_stdout >>> sys.argv = 'test -u -t test_one.TestNotMuch --auto-progress'.split() >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 6 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Disabling progress indication ----------------------------- If -p or --progress have been previously provided on the command line (perhaps by a wrapper script) but you do not desire progress indication, you can switch it off with --no-progress: >>> sys.argv = 'test -u -t test_one.TestNotMuch -p --no-progress'.split() >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 6 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-repeat.rst0000644000076600000240000000367415011314374025705 0ustar00m.howitzstaffRepeating Tests =============== The --repeat option can be used to repeat tests some number of times. Repeating tests is useful to help make sure that tests clean up after themselves. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test --layer 112 --layer UnitTests --repeat 3'.split() >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Iteration 1 Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Iteration 2 Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Iteration 3 Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer112 tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up samplelayers.Layerx in 0.000 seconds. Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer11 in 0.000 seconds. Set up samplelayers.Layer112 in 0.000 seconds. Iteration 1 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.010 seconds. Iteration 2 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.010 seconds. Iteration 3 Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.010 seconds. Tearing down left over layers: Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 182 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False The tests are repeated by layer. Layers are set up and torn down only once. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-report-skipped.rst0000644000076600000240000000547515011314374027376 0ustar00m.howitzstafftestrunner handling of skipped tests ==================================== ``unittest`` provides a way to "skip" tests, see http://docs.python.org/2/library/unittest.html#skipping-tests-and-expected-failures This feature is reported by the test runner. :: >>> import os, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex-skip') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sample_skipped_tests$', ... ] >>> sys.argv = ['test'] Show how many skipped tests has been run ---------------------------------------- By default, the runner displays how many tests has been skipped, as part of the result line:: >>> testrunner.run_internal(defaults + ["-t", "TestSkipppedNoLayer"]) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 1 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False If tests has been skipped in a layer, they are reported in the layer's status line, and in the total status line:: >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in 0.000 seconds. Ran 1 tests with 0 failures, 0 errors and 1 skipped in 0.000 seconds. Running sample_skipped_tests.Layer tests: Tear down zope.testrunner.layer.UnitTests in 0.000 seconds. Set up sample_skipped_tests.Layer in 0.000 seconds. Ran 2 tests with 0 failures, 0 errors and 1 skipped in 0.000 seconds. Tearing down left over layers: Tear down sample_skipped_tests.Layer in 0.000 seconds. Total: 3 tests, 0 failures, 0 errors and 2 skipped in 0.034 seconds. False Show the reason why tests have been skipped ------------------------------------------- By changing the verbose level of the runner itself, you can get increasing number of information:: >>> testrunner.run_internal(defaults + ["-t", "TestSkipppedNoLayer"]) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 1 tests with 0 failures, 0 errors and 1 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False >>> testrunner.run_internal(defaults + ["-t", "TestSkipppedNoLayer", "-vv"]) Running tests at level 1 ... test_skipped (sample_skipped_tests.TestSkipppedNoLayer...) (skipped) ... >>> testrunner.run_internal(defaults + ["-t", "TestSkipppedNoLayer", "-vvv"]) Running tests at level 1 ... test_skipped (sample_skipped_tests.TestSkipppedNoLayer...) (skipped: I'm a skipped test!) ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-shuffle.rst0000644000076600000240000001356415011314374026060 0ustar00m.howitzstaff================= Shuffling tests ================= By default, every time you launch the testrunner it will run the tests in a specific order. However, if you want to ensure that your tests are well isolated then running them in a varying order can be helpful. (Proper isolation meaning your tests don't depend on each others outcomes or side effects and thus the setup and tear down methods are written properly.) The ``--shuffle`` option tells the test runner to shuffle the list of tests before running them: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> default_argv = 'test -u -m sample1 -t test_y0 --list-tests ' Running shuffled tests ====================== When specifying the ``--shuffle`` option tests are ordered differently each time you run the tests: >>> argv = (default_argv + '--shuffle').split() >>> testrunner.run_internal(defaults, argv) Tests were shuffled using seed number ... Listing zope.testrunner.layer.UnitTests tests: ... False .. note:: The runner prints out a new piece of information which is the seed number used to generate the shuffled list of tests. This seed number can later be used to re-run the tests in exactly the same order to support debugging. Specifying a seed number to control tests shuffling =================================================== Along with the ``--shuffle`` option comes the ``--shuffle-seed`` option which takes a seed number as an argument. If you want to reproduce test failures that happened during a randomly shuffled test run then simply write down the seed number that was printed out and run it again using the ``--shuffle-seed`` option. The order is guaranteed to be the same. .. note:: There was an issue with the guaranteed order in zope.testrunner before 4.0.2: the order could change depending on dictionary iteration order, if you had multiple test layers. This bug was fixed in 4.0.2, but a side effect of the fix means that the new guaranteed order is likely to be different from what you used to get with an older zope.testrunner. For example, using the seed number 0 will give us the following, stable, list of tests: >>> argv = (default_argv + '--shuffle --shuffle-seed 0').split() >>> testrunner.run_internal(defaults, argv) Tests were shuffled using seed number 0. Listing zope.testrunner.layer.UnitTests tests: test_y0 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sampletests.test1) False >>> testrunner.run_internal(defaults, argv) Tests were shuffled using seed number 0. Listing zope.testrunner.layer.UnitTests tests: test_y0 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sampletests.test1) False Whereas using the seed number 42 will give us the following, different but stable, list of tests: >>> argv = (default_argv + '--shuffle --shuffle-seed 42').split() >>> testrunner.run_internal(defaults, argv) Tests were shuffled using seed number 42. Listing zope.testrunner.layer.UnitTests tests: test_y0 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sampletests.test_one) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletests.test_one.TestA...) False >>> testrunner.run_internal(defaults, argv) Tests were shuffled using seed number 42. Listing zope.testrunner.layer.UnitTests tests: test_y0 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sampletests.test_one) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletests.test_one.TestA...) False Selecting a seed number without ``--shuffle`` --------------------------------------------- Note that the ``--shuffle-seed`` option must be used along with ``--shuffle`` option or tests will not be re-ordered: >>> argv = (default_argv + '--shuffle-seed 42').split() >>> testrunner.run_internal(defaults, argv) Listing zope.testrunner.layer.UnitTests tests: test_y0 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sampletests.test_one) False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-simple.rst0000644000076600000240000001020715011314374025704 0ustar00m.howitzstaff================= Basic API Usage ================= .. currentmodule:: zope.testrunner The test runner consists of an importable module. The test runner is used by providing scripts that import and invoke the `run` method from the module. The `testrunner` module is controlled via command-line options. Test scripts supply base and default options by supplying a list of default command-line options that are processed before the user-supplied command-line options are provided. Typically, a test script does 2 things: - Adds the directory containing the zope package to the Python path. - Calls the test runner with default arguments and arguments supplied to the script. Normally, it just passes default/setup arguments. The test runner uses `sys.argv` to get the user's input. This testrunner_ex subdirectory contains a number of sample packages with tests. Let's run the tests found here. First though, we'll set up our default options: >>> import os.path >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] The default options are used by a script to customize the test runner for a particular application. In this case, we use two options: path Set the path where the test runner should look for tests. This path is also added to the Python path. tests-pattern Tell the test runner how to recognize modules or packages containing tests. Now, if we run the tests, without any other options: >>> from zope import testrunner >>> import sys >>> sys.argv = ['test'] >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer1 tests: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Set up samplelayers.Layer1 in N.NNN seconds. Ran 9 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer11 tests: Set up samplelayers.Layer11 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer111 tests: Set up samplelayers.Layerx in N.NNN seconds. Set up samplelayers.Layer111 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer112 tests: Tear down samplelayers.Layer111 in N.NNN seconds. Set up samplelayers.Layer112 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer12 tests: Tear down samplelayers.Layer112 in N.NNN seconds. Tear down samplelayers.Layerx in N.NNN seconds. Tear down samplelayers.Layer11 in N.NNN seconds. Set up samplelayers.Layer12 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer121 tests: Set up samplelayers.Layer121 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running samplelayers.Layer122 tests: Tear down samplelayers.Layer121 in N.NNN seconds. Set up samplelayers.Layer122 in N.NNN seconds. Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in N.NNN seconds. Tear down samplelayers.Layer12 in N.NNN seconds. Tear down samplelayers.Layer1 in N.NNN seconds. Total: 321 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False we see the normal testrunner output, which summarizes the tests run for each layer. For each layer, we see what layers had to be torn down or set up to run the layer and we see the number of tests run, with results. The test runner returns a boolean indicating whether there were errors. In this example, there were no errors, so it returned False. (Of course, the times shown in these examples are just examples. Times will vary depending on system speed.) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-stops-when-stop-on-error.rst0000644000076600000240000001023715011314374031251 0ustar00m.howitzstafftestrunner stops test run when --stop-on-error is used and there's an error =========================================================================== Issue/bug #37 on Github: the test runner stops running tests *for the current layer only* when started with -x/--stop-on-error and a test fails. We want the test runner to stop running all tests. :: >>> import os, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex-37') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^stop_on_error', ... ] >>> sys.argv = ['test'] When we don't pass the flag, we see two layers are tested --------------------------------------------------------- >>> testrunner.run_internal(defaults) Running layers.LayerA tests: Set up layers.LayerA in N.NNN seconds. Failure in test test (stop_on_error.ErrorTestCase1...) Traceback (most recent call last): testrunner-ex-37/stop_on_error.py", Line NNN, in test self.assertTrue(False) AssertionError: False is not true Ran 1 tests with 1 failures, 0 errors and 0 skipped in N.NNN seconds. Running layers.LayerB tests: Tear down layers.LayerA in N.NNN seconds. Set up layers.LayerB in N.NNN seconds. Failure in test test (stop_on_error.ErrorTestCase2...) Traceback (most recent call last): testrunner-ex-37/stop_on_error.py", Line NNN, in test self.assertTrue(False) AssertionError: False is not true Ran 1 tests with 1 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down layers.LayerB in N.NNN seconds. Total: 2 tests, 2 failures, 0 errors and 0 skipped in N.NNN seconds. True When we do pass the flag we see only one layer is tested -------------------------------------------------------- >>> testrunner.run_internal(defaults + ["--stop-on-error"]) Running layers.LayerA tests: Set up layers.LayerA in N.NNN seconds. Failure in test test (stop_on_error.ErrorTestCase1...) Traceback (most recent call last): testrunner-ex-37/stop_on_error.py", Line NNN, in test self.assertTrue(False) AssertionError: False is not true Ran 1 tests with 1 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down layers.LayerA in N.NNN seconds. True Same for failures, without the flag, we see two layers ------------------------------------------------------ >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^stop_on_failure', ... ] >>> testrunner.run_internal(defaults) Running layers.LayerA tests: Set up layers.LayerA in N.NNN seconds. Error in test test (stop_on_failure.FailureTestCase1...) Traceback (most recent call last): testrunner-ex-37/stop_on_failure.py", Line NNN, in test raise Exception Exception Ran 1 tests with 0 failures, 1 errors and 0 skipped in N.NNN seconds. Running layers.LayerB tests: Tear down layers.LayerA in N.NNN seconds. Set up layers.LayerB in N.NNN seconds. Error in test test (stop_on_failure.FailureTestCase2...) Traceback (most recent call last): testrunner-ex-37/stop_on_failure.py", Line NNN, in test raise Exception Exception Ran 1 tests with 0 failures, 1 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down layers.LayerB in N.NNN seconds. Total: 2 tests, 0 failures, 2 errors and 0 skipped in N.NNN seconds. True When we do pass the flag we see only one layer is tested -------------------------------------------------------- >>> testrunner.run_internal(defaults + ["--stop-on-error"]) Running layers.LayerA tests: Set up layers.LayerA in N.NNN seconds. Error in test test (stop_on_failure.FailureTestCase1...) Traceback (most recent call last): testrunner-ex-37/stop_on_failure.py", Line NNN, in test raise Exception Exception Ran 1 tests with 0 failures, 1 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down layers.LayerA in N.NNN seconds. True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-subprocess-errors.rst0000644000076600000240000001346115011314374030122 0ustar00m.howitzstaffThis is a test for error handling when the test runner runs a test layer in a subprocess. Fake an IOError reading the output of the subprocess to exercise the reporting of that error: >>> class FakeStdout(object): ... raised = False ... def __init__(self, msg): ... self.msg = msg ... def readline(self): ... if not self.raised: ... self.raised = True ... raise IOError(self.msg) >>> class FakeStderr(object): ... def __init__(self, msg): ... self.msg = msg ... def read(self): ... return self.msg >>> class FakeProcess(object): ... def __init__(self, out, err): ... self.stdout = FakeStdout(out) ... self.stderr = FakeStderr(err) ... def kill(self): ... pass ... communicate = kill >>> class FakePopen(object): ... def __init__(self, out, err): ... self.out = out ... self.err = err ... def __call__(self, *args, **kw): ... return FakeProcess(self.out, self.err) >>> import subprocess >>> Popen = subprocess.Popen >>> subprocess.Popen = FakePopen( ... "Failure triggered to verify error reporting", ... "0 0 0") >>> import os, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... ] >>> argv = [sys.argv[0], ... '-vv', '--tests-pattern', '^sampletests_buffering.*'] >>> _ = testrunner.run_internal(defaults, argv) Running tests at level 1 Running sampletests_buffering.Layer1 tests: Set up sampletests_buffering.Layer1 in N.NNN seconds. Running: test_something (sampletests_buffering.TestSomething1...) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sampletests_buffering.Layer2 tests: Tear down sampletests_buffering.Layer1 ... not supported Error reading subprocess output for sampletests_buffering.Layer2 Failure triggered to verify error reporting Total: 1 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. Now fake some unexpected stderr to test reporting a failure when communicating with the subprocess: >>> subprocess.Popen = FakePopen( ... "Failure triggered to verify error reporting", ... b"segmentation fault (core dumped muahahaha)") >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... ] >>> argv = [sys.argv[0], ... '-vv', '--tests-pattern', '^sampletests_buffering.*'] >>> _ = testrunner.run_internal(defaults, argv) # doctest: +ELLIPSIS Running tests at level 1 Running sampletests_buffering.Layer1 tests: Set up sampletests_buffering.Layer1 in N.NNN seconds. Running: test_something (sampletests_buffering.TestSomething1...) Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sampletests_buffering.Layer2 tests: Tear down sampletests_buffering.Layer1 ... not supported Error reading subprocess output for sampletests_buffering.Layer2 Failure triggered to verify error reporting ********************************************************************** Could not communicate with subprocess! Child command line: ['...', '--resume-layer', 'sampletests_buffering.Layer2', '0', '--default', '--path', '--default', 'testrunner-ex', '-vv', '--tests-pattern', '^sampletests_buffering.*'] Child stderr was: segmentation fault (core dumped muahahaha) ********************************************************************** Tests with errors: subprocess for sampletests_buffering.Layer2 Total: 1 tests, 0 failures, 1 errors and 0 skipped in N.NNN seconds. Very large subprocess output is trimmed, unless you ask for extra verbosity >>> subprocess.Popen = FakePopen( ... "Failure triggered to verify error reporting", ... "\n".join(str(n) for n in range(1, 101)).encode()) >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... ] >>> argv = [sys.argv[0], ... '-v', '--tests-pattern', '^sampletests_buffering.*'] >>> _ = testrunner.run_internal(defaults, argv) # doctest: +ELLIPSIS Running tests at level 1 Running sampletests_buffering.Layer1 tests: Set up sampletests_buffering.Layer1 in N.NNN seconds. Running: . Ran 1 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Running sampletests_buffering.Layer2 tests: Tear down sampletests_buffering.Layer1 ... not supported Error reading subprocess output for sampletests_buffering.Layer2 Failure triggered to verify error reporting ********************************************************************** Could not communicate with subprocess! Child command line: ['...', '--resume-layer', 'sampletests_buffering.Layer2', '0', '--default', '--path', '--default', 'testrunner-ex', '-v', '--tests-pattern', '^sampletests_buffering.*'] Child stderr was: 1 2 3 4 5 6 7 8 9 10 ... 91 92 93 94 95 96 97 98 99 100 ********************************************************************** Tests with errors: subprocess for sampletests_buffering.Layer2 Total: 1 tests, 0 failures, 1 errors and 0 skipped in N.NNN seconds. >>> subprocess.Popen = Popen ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-subtest.rst0000644000076600000240000000273215011314374026110 0ustar00m.howitzstaff===================== ``subTest`` Support ===================== The testrunner supports ``unittest.TestCase.subTest``. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> from zope import testrunner >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', 'subtest', ... ] >>> from zope import testrunner >>> sys.argv = 'test -vv'.split() >>> x = testrunner.run_internal(defaults) # doctest: +ELLIPSIS Running tests at level 1 ... Running: test_subTest (subtest.TestSomething...) Failure in test test_subTest (subtest.TestSomething...) [fail 1] Traceback (most recent call last): testrunner-ex/subtest.py", Line NNN, in test_subTest self.assertEqual(0, 1) AssertionError: 0 != 1 Failure in test test_subTest (subtest.TestSomething...) [fail 2] Traceback (most recent call last): testrunner-ex/subtest.py", Line NNN, in test_subTest self.assertEqual(0, 1) AssertionError: 0 != 1 Ran 1 tests with 2 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. Tests with failures: test_subTest (subtest.TestSomething...) [fail 1] test_subTest (subtest.TestSomething...) [fail 2] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-subunit-err.rst0000644000076600000240000000163315011314374026675 0ustar00m.howitzstaffUsing subunit output without subunit installed ============================================== To use the --subunit and --subunit-v2 reporting options, you must have subunit installed. If you do not, you will get an error message: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner >>> sys.argv = 'test --subunit'.split() >>> _ = testrunner.run_internal(defaults) Subunit is not installed. Please install Subunit to generate subunit output. >>> sys.argv = 'test --subunit-v2'.split() >>> _ = testrunner.run_internal(defaults) Subunit is not installed. Please install Subunit to generate subunit output. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-subunit-leaks.rst0000644000076600000240000000672615011314374027214 0ustar00m.howitzstaffDebugging Memory Leaks with subunit output ========================================== The --report-refcounts (-r) option can be used with the --repeat (-N) option to detect and diagnose memory leaks. To use this option, you must configure Python with the --with-pydebug option. (On Unix, pass this option to configure and then build Python.) For more detailed documentation, see testrunner-leaks.txt. >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner Each layer is repeated the requested number of times. For each iteration after the first, the system refcount and change in system refcount is shown. The system refcount is the total of all refcount in the system. When a refcount on any object is changed, the system refcount is changed by the same amount. Tests that don't leak show zero changes in system refcount. Let's look at an example test that leaks: >>> sys.argv = 'test --subunit --tests-pattern leak -N2 -r'.split() >>> _ = testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:setUp tags: zope:layer:zope.testrunner.layer.UnitTests test: leak.TestSomething.testleak successful: leak.TestSomething.testleak test: leak.TestSomething.testleak successful: leak.TestSomething.testleak test: zope:refcounts tags: zope:refcounts successful: zope:refcounts [ multipart Content-Type: text/plain;charset=utf8 ... ...\r ...\r Content-Type: text/plain;charset=utf8 ... ...\r ...\r ] tags: -zope:layer:zope.testrunner.layer.UnitTests time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:tearDown Here we see that the system refcount is increasing. If we specify a verbosity greater than one, we can get details broken out by object type (or class): >>> sys.argv = 'test --subunit --tests-pattern leak -N2 -v -r'.split() >>> _ = testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:setUp tags: zope:layer:zope.testrunner.layer.UnitTests test: leak.TestSomething.testleak successful: leak.TestSomething.testleak test: leak.TestSomething.testleak successful: leak.TestSomething.testleak test: zope:refcounts tags: zope:refcounts successful: zope:refcounts [ multipart Content-Type: text/plain;charset=utf8 ... ...\r ...\r Content-Type: text/plain;charset=utf8 ... ...\r ...\r Content-Type: text/plain;charset=utf8 ... ...\r ... ] tags: -zope:layer:zope.testrunner.layer.UnitTests time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:tearDown ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-subunit-v2.rst0000644000076600000240000010027115011314374026432 0ustar00m.howitzstaffSubunit v2 Output ================= Subunit supports two protocols: v1 is largely a text protocol while v2 is binary. v2 is generally more robust, but tools that consume the output of zope.testrunner may not expect to receive it, so we support emitting either protocol. First we set up some defaults: >>> import os.path, sys >>> defaults = [ ... '--subunit-v2', ... '--path', os.path.join(this_directory, 'testrunner-ex'), ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner For easier doctesting, we use a helper that summarizes the output. >>> from subunit import ByteStreamToStreamResult >>> from testtools import StreamResult >>> class SummarizeResult(StreamResult): ... def __init__(self, stream): ... self.stream = stream ... self._last_file = None ... def status(self, test_id=None, test_status=None, test_tags=None, ... runnable=True, file_name=None, file_bytes=None, ... eof=False, mime_type=None, route_code=None, ... timestamp=None): ... if (test_id, file_name) == self._last_file: ... self.stream.write(file_bytes.tobytes().decode('utf-8', 'replace')) ... return ... elif self._last_file is not None: ... self.stream.write('\n') ... self._last_file = None ... if test_id is not None: ... self.stream.write('id=%s ' % (test_id,)) ... if test_status is not None: ... self.stream.write('status=%s ' % (test_status,)) ... if test_tags is not None: ... self.stream.write( ... 'tags=(%s) ' % ' '.join(sorted(test_tags))) ... if not runnable: ... self.stream.write('!runnable ') ... self.stream.write('\n') ... if file_name is not None: ... self.stream.write('%s (%s)\n' % (file_name, mime_type)) ... self.stream.write(file_bytes.tobytes().decode('utf-8', 'replace')) ... self._last_file = (test_id, file_name) ... self.stream.flush() >>> def subunit_summarize(func, *args, **kwargs): ... orig_stdout = sys.stdout ... try: ... import io ... sys.stdout = io.TextIOWrapper(io.BytesIO(), encoding='utf-8') ... ret = func(*args, **kwargs) ... sys.stdout.flush() ... buf = sys.stdout ... buf = buf.detach() ... buf.seek(0) ... finally: ... sys.stdout = orig_stdout ... case = ByteStreamToStreamResult(buf, non_subunit_name='stdout') ... case.run(SummarizeResult(sys.stdout)) ... return ret Basic output ------------ There is an 'inprogress' status event for the start of each test and a 'success' status event for each successful test. Zope layer setup and teardown events are represented as tests tagged with 'zope:layer'. This allows them to be distinguished from actual tests, provides a place for the layer timing information in the subunit stream and allows us to include error information if necessary. Once the layer is set up, all future tests are tagged with 'zope:layer:LAYER_NAME'. >>> sys.argv = 'test --layer 122 -t TestNotMuch'.split() >>> subunit_summarize(testrunner.run_internal, defaults) id=samplelayers.Layer1:setUp status=inprogress !runnable id=samplelayers.Layer1:setUp status=success tags=(zope:layer) !runnable id=samplelayers.Layer12:setUp status=inprogress !runnable id=samplelayers.Layer12:setUp status=success tags=(zope:layer zope:layer:samplelayers.Layer1) !runnable id=samplelayers.Layer122:setUp status=inprogress !runnable id=samplelayers.Layer122:setUp status=success tags=(zope:layer zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12) !runnable id=sample1.sampletests.test122.TestNotMuch.test_1 status=inprogress id=sample1.sampletests.test122.TestNotMuch.test_1 status=success tags=(zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12 zope:layer:samplelayers.Layer122) id=sample1.sampletests.test122.TestNotMuch.test_2 status=inprogress id=sample1.sampletests.test122.TestNotMuch.test_2 status=success tags=(zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12 zope:layer:samplelayers.Layer122) id=sample1.sampletests.test122.TestNotMuch.test_3 status=inprogress id=sample1.sampletests.test122.TestNotMuch.test_3 status=success tags=(zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12 zope:layer:samplelayers.Layer122) id=sampletests.test122.TestNotMuch.test_1 status=inprogress id=sampletests.test122.TestNotMuch.test_1 status=success tags=(zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12 zope:layer:samplelayers.Layer122) id=sampletests.test122.TestNotMuch.test_2 status=inprogress id=sampletests.test122.TestNotMuch.test_2 status=success tags=(zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12 zope:layer:samplelayers.Layer122) id=sampletests.test122.TestNotMuch.test_3 status=inprogress id=sampletests.test122.TestNotMuch.test_3 status=success tags=(zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12 zope:layer:samplelayers.Layer122) id=samplelayers.Layer122:tearDown status=inprogress !runnable id=samplelayers.Layer122:tearDown status=success tags=(zope:layer zope:layer:samplelayers.Layer1 zope:layer:samplelayers.Layer12) !runnable id=samplelayers.Layer12:tearDown status=inprogress !runnable id=samplelayers.Layer12:tearDown status=success tags=(zope:layer zope:layer:samplelayers.Layer1) !runnable id=samplelayers.Layer1:tearDown status=inprogress !runnable id=samplelayers.Layer1:tearDown status=success tags=(zope:layer) !runnable False Listing tests ------------- To list tests in subunit v2, we emit a stream of test results using the 'exists' status without actually running the tests. Note that in this stream, we don't emit fake tests for the layer set up and tear down, because it simply doesn't happen. We also don't include the dependent layers in the stream (in this case Layer1 and Layer12), since they are not provided to the reporter. >>> sys.argv = 'test --layer 122 --list-tests -t TestNotMuch'.split() >>> subunit_summarize(testrunner.run_internal, defaults) id=sample1.sampletests.test122.TestNotMuch.test_1 status=inprogress id=sample1.sampletests.test122.TestNotMuch.test_1 status=exists tags=(zope:layer:samplelayers.Layer122) id=sample1.sampletests.test122.TestNotMuch.test_2 status=inprogress id=sample1.sampletests.test122.TestNotMuch.test_2 status=exists tags=(zope:layer:samplelayers.Layer122) id=sample1.sampletests.test122.TestNotMuch.test_3 status=inprogress id=sample1.sampletests.test122.TestNotMuch.test_3 status=exists tags=(zope:layer:samplelayers.Layer122) id=sampletests.test122.TestNotMuch.test_1 status=inprogress id=sampletests.test122.TestNotMuch.test_1 status=exists tags=(zope:layer:samplelayers.Layer122) id=sampletests.test122.TestNotMuch.test_2 status=inprogress id=sampletests.test122.TestNotMuch.test_2 status=exists tags=(zope:layer:samplelayers.Layer122) id=sampletests.test122.TestNotMuch.test_3 status=inprogress id=sampletests.test122.TestNotMuch.test_3 status=exists tags=(zope:layer:samplelayers.Layer122) False Profiling tests --------------- Test suites often cover a lot of code, and the performance of test suites themselves is often a critical part of the development process. Thus, it's good to be able to profile a test run. >>> import tempfile >>> tempdir = tempfile.mkdtemp(prefix='zope.testrunner-test-') >>> sys.argv = [ ... 'test', '--layer=122', '--profile=cProfile', ... '--profile-directory', tempdir, ... '-t', 'TestNotMuch'] >>> subunit_summarize(testrunner.run_internal, defaults) id=samplelayers.Layer1:setUp status=inprogress !runnable ... id=samplelayers.Layer1:tearDown status=success tags=(zope:layer) !runnable id=zope:profiler_stats status=inprogress !runnable id=zope:profiler_stats !runnable profiler-stats (application/x-binary-profile) ...\r ... id=zope:profiler_stats status=success tags=(zope:profiler_stats) !runnable False >>> import shutil >>> shutil.rmtree(tempdir) Errors ------ Errors are recorded in the subunit stream as MIME-encoded chunks of text. (With subunit v2, errors and failures are unfortunately conflated: see https://bugs.launchpad.net/subunit/+bug/1740158.) >>> sys.argv = ['test', '--tests-pattern', '^sampletests_e$'] >>> subunit_summarize(testrunner.run_internal, defaults) id=zope.testrunner.layer.UnitTests:setUp status=inprogress !runnable id=zope.testrunner.layer.UnitTests:setUp status=success tags=(zope:layer) !runnable id=sample2.sampletests_e.eek status=inprogress id=sample2.sampletests_e.eek traceback (text/x-traceback...) Failed doctest test for sample2.sampletests_e.eek testrunner-ex/sample2/sampletests_e.py", Line NNN, in eek ---------------------------------------------------------------------- File testrunner-ex/sample2/sampletests_e.py", Line NNN, in sample2.sampletests_e.eek Failed example: f() Exception raised: Traceback (most recent call last): File "", Line NNN, in ? f() testrunner-ex/sample2/sampletests_e.py", Line NNN, in f g() testrunner-ex/sample2/sampletests_e.py", Line NNN, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined id=sample2.sampletests_e.eek status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.sampletests_e.Test.test1 status=inprogress id=sample2.sampletests_e.Test.test1 status=success tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.sampletests_e.Test.test2 status=inprogress id=sample2.sampletests_e.Test.test2 status=success tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.sampletests_e.Test.test3 status=inprogress id=sample2.sampletests_e.Test.test3 traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample2/sampletests_e.py", Line NNN, in test3 f() testrunner-ex/sample2/sampletests_e.py", Line NNN, in f g() testrunner-ex/sample2/sampletests_e.py", Line NNN, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined id=sample2.sampletests_e.Test.test3 status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.sampletests_e.Test.test4 status=inprogress id=sample2.sampletests_e.Test.test4 status=success tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.sampletests_e.Test.test5 status=inprogress id=sample2.sampletests_e.Test.test5 status=success tags=(zope:layer:zope.testrunner.layer.UnitTests) id=e_rst status=inprogress id=e_rst traceback (text/x-traceback...) Failed doctest test for e.rst testrunner-ex/sample2/e.rst", line 0 ---------------------------------------------------------------------- File testrunner-ex/sample2/e.rst", Line NNN, in e.rst Failed example: f() Exception raised: Traceback (most recent call last): File "", Line NNN, in ? f() File "", Line NNN, in f return x NameError: name 'x' is not defined id=e_rst status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=zope.testrunner.layer.UnitTests:tearDown status=inprogress !runnable id=zope.testrunner.layer.UnitTests:tearDown status=success tags=(zope:layer) !runnable True Capturing output ---------------- To avoid corrupting subunit streams, any output on stdout and stderr is buffered; for failing and erroring tests, it is recorded in the subunit stream as MIME-encoded chunks of text. >>> sys.argv = 'test -ssample2 --tests-pattern ^stdstreamstest$'.split() >>> subunit_summarize(testrunner.run_internal, defaults) id=zope.testrunner.layer.UnitTests:setUp status=inprogress !runnable id=zope.testrunner.layer.UnitTests:setUp status=success tags=(zope:layer) !runnable id=sample2.stdstreamstest.Test.test_stderr_error status=inprogress id=sample2.stdstreamstest.Test.test_stderr_error test-stderr (text/plain; charset="utf8") stderr output on error stderr buffer output on error id=sample2.stdstreamstest.Test.test_stderr_error traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stderr_error raise Exception("boom") Exception: boom id=sample2.stdstreamstest.Test.test_stderr_error status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.stdstreamstest.Test.test_stderr_failure status=inprogress id=sample2.stdstreamstest.Test.test_stderr_failure test-stderr (text/plain; charset="utf8") stderr output on failure stderr buffer output on failure id=sample2.stdstreamstest.Test.test_stderr_failure traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stderr_failure self.assertTrue(False) AssertionError: False is not true id=sample2.stdstreamstest.Test.test_stderr_failure status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.stdstreamstest.Test.test_stderr_success status=inprogress id=sample2.stdstreamstest.Test.test_stderr_success status=success tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.stdstreamstest.Test.test_stdout_error status=inprogress id=sample2.stdstreamstest.Test.test_stdout_error test-stdout (text/plain; charset="utf8") stdout output on error stdout buffer output on error id=sample2.stdstreamstest.Test.test_stdout_error traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stdout_error raise Exception("boom") Exception: boom id=sample2.stdstreamstest.Test.test_stdout_error status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.stdstreamstest.Test.test_stdout_failure status=inprogress id=sample2.stdstreamstest.Test.test_stdout_failure test-stdout (text/plain; charset="utf8") stdout output on failure stdout buffer output on failure id=sample2.stdstreamstest.Test.test_stdout_failure traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stdout_failure self.assertTrue(False) AssertionError: False is not true id=sample2.stdstreamstest.Test.test_stdout_failure status=fail tags=(zope:layer:zope.testrunner.layer.UnitTests) id=sample2.stdstreamstest.Test.test_stdout_success status=inprogress id=sample2.stdstreamstest.Test.test_stdout_success status=success tags=(zope:layer:zope.testrunner.layer.UnitTests) id=zope.testrunner.layer.UnitTests:tearDown status=inprogress !runnable id=zope.testrunner.layer.UnitTests:tearDown status=success tags=(zope:layer) !runnable True Layers that can't be torn down ------------------------------ A layer can have a tearDown method that raises NotImplementedError. If this is the case, the subunit stream will say that the layer skipped its tearDown. >>> sys.argv = 'test -ssample2 --tests-pattern sampletests_ntd$'.split() >>> subunit_summarize(testrunner.run_internal, defaults) id=sample2.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample2.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample2.sampletests_ntd.TestSomething.test_something status=inprogress id=sample2.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample2.sampletests_ntd.Layer) id=sample2.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample2.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample2.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable False Layer failures -------------- If a layer's setUp or tearDown method fails in some other way, this is shown in the subunit stream. >>> sys.argv = 'test --tests-pattern ^brokenlayer$'.split() >>> subunit_summarize(testrunner.run_internal, defaults) id=brokenlayer.BrokenSetUpLayer:setUp status=inprogress !runnable id=brokenlayer.BrokenSetUpLayer:setUp traceback (text/x-traceback...) Traceback (most recent call last): ... ValueError: No value is good enough for me! id=brokenlayer.BrokenSetUpLayer:setUp status=fail tags=(zope:layer) ... id=brokenlayer.BrokenTearDownLayer:tearDown status=inprogress !runnable id=brokenlayer.BrokenTearDownLayer:tearDown traceback (text/x-traceback...) Traceback (most recent call last): ... TypeError: You are not my type. No-one is my type! id=brokenlayer.BrokenTearDownLayer:tearDown status=fail tags=(zope:layer) True Module import errors -------------------- We report module import errors too. They get encoded as tests with errors. The name of the test is the module that could not be imported, the test's result is an error containing the traceback. These "tests" are tagged with zope:import_error. Let's run tests including a module with some bad syntax: >>> sys.argv = [ ... 'test', '--tests-pattern', '^(badsyntax|sampletests(f|_i)?)$', ... '--layer', '1'] >>> subunit_summarize(testrunner.run_internal, defaults) id=sample2.badsyntax status=inprogress id=sample2.badsyntax traceback (text/x-traceback...)... File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/badsyntax.py", line 16 importx unittest # noqa: E999 ...^ SyntaxError: invalid syntax... id=sample2.badsyntax status=fail tags=(zope:import_error) id=sample2.sample21.sampletests_i status=inprogress id=sample2.sample21.sampletests_i traceback (text/x-traceback...) Traceback (most recent call last): File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample21/sampletests_i.py", line 16, in import zope.testrunner.huh # noqa: F401... ModuleNotFoundError: No module named 'zope.testrunner.huh' id=sample2.sample21.sampletests_i status=fail tags=(zope:import_error) id=sample2.sample23.sampletests_i status=inprogress id=sample2.sample23.sampletests_i traceback (text/x-traceback...) Traceback (most recent call last): File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample23/sampletests_i.py", line 17, in class Test(unittest.TestCase): ... raise TypeError('eek') TypeError: eek id=sample2.sample23.sampletests_i status=fail tags=(zope:import_error) id=samplelayers.Layer1:setUp status=inprogress... ... True Tests in subprocesses --------------------- If the tearDown method raises NotImplementedError and there are remaining layers to run, the test runner will restart itself as a new process, resuming tests where it left off: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntd$'] >>> subunit_summarize(testrunner.run_internal, defaults) id=sample1.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample1.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample1.sampletests_ntd.TestSomething.test_something status=inprogress id=sample1.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample1.sampletests_ntd.Layer) id=sample1.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample1.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample1.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable id=Running in a subprocess. status=inprogress !runnable id=Running in a subprocess. status=success tags=(zope:info_suboptimal) !runnable id=sample2.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample2.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample2.sampletests_ntd.TestSomething.test_something status=inprogress id=sample2.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample2.sampletests_ntd.Layer) id=sample2.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample2.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample2.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable id=Running in a subprocess. status=inprogress !runnable id=Running in a subprocess. status=success tags=(zope:info_suboptimal) !runnable id=sample3.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample3.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample3.sampletests_ntd.TestSomething.test_error1 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_error1 traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error1 raise TypeError("Can we see errors") TypeError: Can we see errors id=sample3.sampletests_ntd.TestSomething.test_error1 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.TestSomething.test_error2 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_error2 traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error2 raise TypeError("I hope so") TypeError: I hope so id=sample3.sampletests_ntd.TestSomething.test_error2 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.TestSomething.test_fail1 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_fail1 traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail1 self.assertEqual(1, 2) AssertionError: 1 != 2 id=sample3.sampletests_ntd.TestSomething.test_fail1 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.TestSomething.test_fail2 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_fail2 traceback (text/x-traceback...) Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail2 self.assertEqual(1, 3) AssertionError: 1 != 3 id=sample3.sampletests_ntd.TestSomething.test_fail2 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.TestSomething.test_something status=inprogress id=sample3.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.TestSomething.test_something_else status=inprogress id=sample3.sampletests_ntd.TestSomething.test_something_else status=success tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample3.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample3.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable True Note that debugging doesn't work when running tests in a subprocess: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntd$', ... '-D', ] >>> subunit_summarize(testrunner.run_internal, defaults) id=sample1.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample1.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample1.sampletests_ntd.TestSomething.test_something status=inprogress id=sample1.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample1.sampletests_ntd.Layer) id=sample1.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample1.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample1.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable id=Running in a subprocess. status=inprogress !runnable id=Running in a subprocess. status=success tags=(zope:info_suboptimal) !runnable id=sample2.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample2.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample2.sampletests_ntd.TestSomething.test_something status=inprogress id=sample2.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample2.sampletests_ntd.Layer) id=sample2.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample2.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample2.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable id=Running in a subprocess. status=inprogress !runnable id=Running in a subprocess. status=success tags=(zope:info_suboptimal) !runnable id=sample3.sampletests_ntd.Layer:setUp status=inprogress !runnable id=sample3.sampletests_ntd.Layer:setUp status=success tags=(zope:layer) !runnable id=sample3.sampletests_ntd.TestSomething.test_error1 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_error1 traceback (text/x-traceback...) Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 42, in test_error1 raise TypeError("Can we see errors") TypeError: Can we see errors id=sample3.sampletests_ntd.TestSomething.test_error1 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=Can't post-mortem debug when running a layer as a subprocess! status=inprogress !runnable id=Can't post-mortem debug when running a layer as a subprocess! status=success tags=(zope:error_with_banner zope:layer:sample3.sampletests_ntd.Layer) !runnable id=sample3.sampletests_ntd.TestSomething.test_error2 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_error2 traceback (text/x-traceback...) Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 45, in test_error2 raise TypeError("I hope so") TypeError: I hope so id=sample3.sampletests_ntd.TestSomething.test_error2 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=Can't post-mortem debug when running a layer as a subprocess! status=inprogress !runnable id=Can't post-mortem debug when running a layer as a subprocess! status=success tags=(zope:error_with_banner zope:layer:sample3.sampletests_ntd.Layer) !runnable id=sample3.sampletests_ntd.TestSomething.test_fail1 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_fail1 traceback (text/x-traceback...) Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 48, in test_fail1 self.assertEqual(1, 2) File "/usr/lib/python3.11/unittest.py", line 350, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: 1 != 2 id=sample3.sampletests_ntd.TestSomething.test_fail1 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=Can't post-mortem debug when running a layer as a subprocess! status=inprogress !runnable id=Can't post-mortem debug when running a layer as a subprocess! status=success tags=(zope:error_with_banner zope:layer:sample3.sampletests_ntd.Layer) !runnable id=sample3.sampletests_ntd.TestSomething.test_fail2 status=inprogress id=sample3.sampletests_ntd.TestSomething.test_fail2 traceback (text/x-traceback...) Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 51, in test_fail2 self.assertEqual(1, 3) File "/usr/lib/python3.11/unittest.py", line 350, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: 1 != 3 id=sample3.sampletests_ntd.TestSomething.test_fail2 status=fail tags=(zope:layer:sample3.sampletests_ntd.Layer) id=Can't post-mortem debug when running a layer as a subprocess! status=inprogress !runnable id=Can't post-mortem debug when running a layer as a subprocess! status=success tags=(zope:error_with_banner zope:layer:sample3.sampletests_ntd.Layer) !runnable id=sample3.sampletests_ntd.TestSomething.test_something status=inprogress id=sample3.sampletests_ntd.TestSomething.test_something status=success tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.TestSomething.test_something_else status=inprogress id=sample3.sampletests_ntd.TestSomething.test_something_else status=success tags=(zope:layer:sample3.sampletests_ntd.Layer) id=sample3.sampletests_ntd.Layer:tearDown status=inprogress !runnable id=sample3.sampletests_ntd.Layer:tearDown !runnable reason (text/plain...) tearDown not supported id=sample3.sampletests_ntd.Layer:tearDown status=skip tags=(zope:layer) !runnable True Support skipped tests --------------------- >>> directory_with_skipped_tests = os.path.join(this_directory, ... 'testrunner-ex-skip') >>> skip_defaults = [ ... '--path', directory_with_skipped_tests, ... '--tests-pattern', '^sample_skipped_tests$', ... ] >>> sys.argv = ['test'] >>> subunit_summarize( ... testrunner.run_internal, ... skip_defaults + ["--subunit-v2", "-t", "TestSkipppedNoLayer"]) id=zope.testrunner.layer.UnitTests:setUp status=inprogress !runnable id=zope.testrunner.layer.UnitTests:setUp status=success tags=(zope:layer) !runnable id=sample_skipped_tests.TestSkipppedNoLayer.test_skipped status=inprogress id=sample_skipped_tests.TestSkipppedNoLayer.test_skipped reason (text/plain...) I'm a skipped test! id=sample_skipped_tests.TestSkipppedNoLayer.test_skipped status=skip tags=(zope:layer:zope.testrunner.layer.UnitTests) id=zope.testrunner.layer.UnitTests:tearDown status=inprogress !runnable id=zope.testrunner.layer.UnitTests:tearDown status=success tags=(zope:layer) !runnable False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-subunit.rst0000644000076600000240000007242415011314374026115 0ustar00m.howitzstaffSubunit Output ============== Subunit is a streaming protocol for interchanging test results. More information can be found at https://launchpad.net/subunit. To enable support for ``subunit``, install it and its dependencies via the ``zope.testrunner`` extra: $ pip install zope.testrunner[subunit] or directly: $ pip install python-subunit First we set up some defaults: >>> import os.path, sys >>> defaults = [ ... '--subunit', ... '--path', os.path.join(this_directory, 'testrunner-ex'), ... '--tests-pattern', '^sampletestsf?$', ... ] >>> from zope import testrunner Basic output ------------ Subunit output is line-based, with a 'test:' line for the start of each test and a 'successful:' line for each successful test. Zope layer setup and teardown events are represented as tests tagged with 'zope:layer'. This allows them to be distinguished from actual tests, provides a place for the layer timing information in the subunit stream and allows us to include error information if necessary. Once the layer is set up, all future tests are tagged with 'zope:layer:LAYER_NAME'. >>> sys.argv = 'test --layer 122 -t TestNotMuch'.split() >>> testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer1:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer1:setUp tags: zope:layer:samplelayers.Layer1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer12:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer12:setUp tags: zope:layer:samplelayers.Layer12 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer122:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer122:setUp tags: zope:layer:samplelayers.Layer122 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests.test122.TestNotMuch.test_1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample1.sampletests.test122.TestNotMuch.test_1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests.test122.TestNotMuch.test_2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample1.sampletests.test122.TestNotMuch.test_2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests.test122.TestNotMuch.test_3 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample1.sampletests.test122.TestNotMuch.test_3 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sampletests.test122.TestNotMuch.test_1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sampletests.test122.TestNotMuch.test_1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sampletests.test122.TestNotMuch.test_2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sampletests.test122.TestNotMuch.test_2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sampletests.test122.TestNotMuch.test_3 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sampletests.test122.TestNotMuch.test_3 tags: -zope:layer:samplelayers.Layer122 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer122:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer122:tearDown tags: -zope:layer:samplelayers.Layer12 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer12:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer12:tearDown tags: -zope:layer:samplelayers.Layer1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer1:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer1:tearDown False Listing tests ------------- A subunit stream is a stream of test results, more or less, so the most natural way of listing tests in subunit is to simply emit successful test results without actually running the tests. Note that in this stream, we don't emit fake tests for the layer set up and tear down, because it simply doesn't happen. We also don't include the dependent layers in the stream (in this case Layer1 and Layer12), since they are not provided to the reporter. >>> sys.argv = 'test --layer 122 --list-tests -t TestNotMuch'.split() >>> testrunner.run_internal(defaults) tags: zope:layer:samplelayers.Layer122 test: sample1.sampletests.test122.TestNotMuch.test_1 successful: sample1.sampletests.test122.TestNotMuch.test_1 test: sample1.sampletests.test122.TestNotMuch.test_2 successful: sample1.sampletests.test122.TestNotMuch.test_2 test: sample1.sampletests.test122.TestNotMuch.test_3 successful: sample1.sampletests.test122.TestNotMuch.test_3 test: sampletests.test122.TestNotMuch.test_1 successful: sampletests.test122.TestNotMuch.test_1 test: sampletests.test122.TestNotMuch.test_2 successful: sampletests.test122.TestNotMuch.test_2 test: sampletests.test122.TestNotMuch.test_3 successful: sampletests.test122.TestNotMuch.test_3 tags: -zope:layer:samplelayers.Layer122 False Profiling tests --------------- Test suites often cover a lot of code, and the performance of test suites themselves is often a critical part of the development process. Thus, it's good to be able to profile a test run. >>> import tempfile >>> tempdir = tempfile.mkdtemp(prefix='zope.testrunner-test-') >>> sys.argv = [ ... 'test', '--layer=122', '--profile=cProfile', ... '--profile-directory', tempdir, ... '-t', 'TestNotMuch'] >>> testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: samplelayers.Layer1:setUp ... time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: samplelayers.Layer1:tearDown test: zope:profiler_stats tags: zope:profiler_stats successful: zope:profiler_stats [ multipart Content-Type: application/x-binary-profile profiler-stats ...\r ... ] False >>> import shutil >>> shutil.rmtree(tempdir) Errors ------ Errors are recorded in the subunit stream as MIME-encoded chunks of text. >>> sys.argv = ['test', '--tests-pattern', '^sampletests_e$'] >>> testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:setUp tags: zope:layer:zope.testrunner.layer.UnitTests time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_e.eek time: YYYY-MM-DD HH:MM:SS.mmmmmmZ failure: sample2.sampletests_e.eek [ multipart Content-Type: text/x-traceback... traceback NNN\r Failed doctest test for sample2.sampletests_e.eek testrunner-ex/sample2/sampletests_e.py", Line NNN, in eek ---------------------------------------------------------------------- File testrunner-ex/sample2/sampletests_e.py", Line NNN, in sample2.sampletests_e.eek Failed example: f() Exception raised: Traceback (most recent call last): File "", Line NNN, in ? f() testrunner-ex/sample2/sampletests_e.py", Line NNN, in f g() testrunner-ex/sample2/sampletests_e.py", Line NNN, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_e.Test.test1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_e.Test.test1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_e.Test.test2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_e.Test.test2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_e.Test.test3 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample2.sampletests_e.Test.test3 [ multipart Content-Type: text/x-traceback... traceback NNN\r Traceback (most recent call last): testrunner-ex/sample2/sampletests_e.py", Line NNN, in test3 f() testrunner-ex/sample2/sampletests_e.py", Line NNN, in f g() testrunner-ex/sample2/sampletests_e.py", Line NNN, in g x = y + 1 # noqa: F821 - __traceback_info__: I don't know what Y should be. NameError: name 'y' is not defined 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_e.Test.test4 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_e.Test.test4 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_e.Test.test5 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_e.Test.test5 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: e_rst time: YYYY-MM-DD HH:MM:SS.mmmmmmZ failure: e_rst [ multipart Content-Type: text/x-traceback... traceback NNN\r Failed doctest test for e.rst testrunner-ex/sample2/e.rst", line 0 ---------------------------------------------------------------------- File testrunner-ex/sample2/e.rst", Line NNN, in e.rst Failed example: f() Exception raised: Traceback (most recent call last): File "", Line NNN, in ? f() File "", Line NNN, in f return x NameError: name 'x' is not defined 0\r ] tags: -zope:layer:zope.testrunner.layer.UnitTests time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:tearDown True Capturing output ---------------- To avoid corrupting subunit streams, any output on stdout and stderr is buffered; for failing and erroring tests, it is recorded in the subunit stream as MIME-encoded chunks of text. >>> sys.argv = 'test -ssample2 --tests-pattern ^stdstreamstest$'.split() >>> testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:setUp tags: zope:layer:zope.testrunner.layer.UnitTests time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.stdstreamstest.Test.test_stderr_error time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample2.stdstreamstest.Test.test_stderr_error [ multipart Content-Type: text/plain;charset=utf8 test-stderr 35\r stderr output on error stderr buffer output on error 0\r Content-Type: text/x-traceback... traceback NNN\r Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stderr_error raise Exception("boom") Exception: boom 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.stdstreamstest.Test.test_stderr_failure time: YYYY-MM-DD HH:MM:SS.mmmmmmZ failure: sample2.stdstreamstest.Test.test_stderr_failure [ multipart Content-Type: text/plain;charset=utf8 test-stderr 39\r stderr output on failure stderr buffer output on failure 0\r Content-Type: text/x-traceback... traceback NNN\r Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stderr_failure self.assertTrue(False) AssertionError: False is not true 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.stdstreamstest.Test.test_stderr_success time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.stdstreamstest.Test.test_stderr_success time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.stdstreamstest.Test.test_stdout_error time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample2.stdstreamstest.Test.test_stdout_error [ multipart Content-Type: text/plain;charset=utf8 test-stdout 35\r stdout output on error stdout buffer output on error 0\r Content-Type: text/x-traceback... traceback NNN\r Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stdout_error raise Exception("boom") Exception: boom 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.stdstreamstest.Test.test_stdout_failure time: YYYY-MM-DD HH:MM:SS.mmmmmmZ failure: sample2.stdstreamstest.Test.test_stdout_failure [ multipart Content-Type: text/plain;charset=utf8 test-stdout 39\r stdout output on failure stdout buffer output on failure 0\r Content-Type: text/x-traceback... traceback NNN\r Traceback (most recent call last): testrunner-ex/sample2/stdstreamstest.py", Line NNN, in test_stdout_failure self.assertTrue(False) AssertionError: False is not true 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.stdstreamstest.Test.test_stdout_success time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.stdstreamstest.Test.test_stdout_success tags: -zope:layer:zope.testrunner.layer.UnitTests time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: zope.testrunner.layer.UnitTests:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: zope.testrunner.layer.UnitTests:tearDown True Layers that can't be torn down ------------------------------ A layer can have a tearDown method that raises NotImplementedError. If this is the case, the subunit stream will say that the layer skipped its tearDown. >>> sys.argv = 'test -ssample2 --tests-pattern sampletests_ntd$'.split() >>> testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.Layer:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_ntd.Layer:setUp tags: zope:layer:sample2.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_ntd.TestSomething.test_something tags: -zope:layer:sample2.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.Layer:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ skip: sample2.sampletests_ntd.Layer:tearDown [ tearDown not supported ] False Layer failures -------------- If a layer's setUp or tearDown method fails in some other way, this is shown in the subunit stream. >>> sys.argv = 'test --tests-pattern ^brokenlayer$'.split() >>> testrunner.run_internal(defaults) time: ... test: brokenlayer.BrokenSetUpLayer:setUp tags: zope:layer failure: brokenlayer.BrokenSetUpLayer:setUp [ Traceback (most recent call last): ... ValueError: No value is good enough for me! ] time: ... test: brokenlayer.BrokenTearDownLayer:tearDown tags: zope:layer failure: brokenlayer.BrokenTearDownLayer:tearDown [ Traceback (most recent call last): ... TypeError: You are not my type. No-one is my type! ] True Module import errors -------------------- We report module import errors too. They get encoded as tests with errors. The name of the test is the module that could not be imported, the test's result is an error containing the traceback. These "tests" are tagged with zope:import_error. Let's run tests including a module with some bad syntax: >>> sys.argv = [ ... 'test', '--tests-pattern', '^(badsyntax|sampletests(f|_i)?)$', ... '--layer', '1'] >>> testrunner.run_internal(defaults) test: sample2.badsyntax tags: zope:import_error error: sample2.badsyntax [... File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/badsyntax.py", line 16 importx unittest # noqa: E999 ...^ SyntaxError: invalid syntax... ] test: sample2.sample21.sampletests_i tags: zope:import_error error: sample2.sample21.sampletests_i [ Traceback (most recent call last): File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample21/sampletests_i.py", line 16, in import zope.testrunner.huh # noqa: F401... ModuleNotFoundError: No module named 'zope.testrunner.huh' ] test: sample2.sample23.sampletests_i tags: zope:import_error error: sample2.sample23.sampletests_i [ Traceback (most recent call last): File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample23/sampletests_i.py", line 17, in class Test(unittest.TestCase): ... raise TypeError('eek') TypeError: eek ] time: 2010-07-19 21:27:16.708260Z test: samplelayers.Layer1:setUp tags: zope:layer ... True Tests in subprocesses --------------------- If the tearDown method raises NotImplementedError and there are remaining layers to run, the test runner will restart itself as a new process, resuming tests where it left off: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntd$'] >>> testrunner.run_internal(defaults) time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests_ntd.Layer:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample1.sampletests_ntd.Layer:setUp tags: zope:layer:sample1.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample1.sampletests_ntd.TestSomething.test_something tags: -zope:layer:sample1.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests_ntd.Layer:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ skip: sample1.sampletests_ntd.Layer:tearDown [ tearDown not supported ] test: Running in a subprocess. tags: zope:info_suboptimal successful: Running in a subprocess. time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.Layer:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_ntd.Layer:setUp tags: zope:layer:sample2.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_ntd.TestSomething.test_something tags: -zope:layer:sample2.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.Layer:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ skip: sample2.sampletests_ntd.Layer:tearDown [ tearDown not supported ] test: Running in a subprocess. tags: zope:info_suboptimal successful: Running in a subprocess. time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.Layer:setUp tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample3.sampletests_ntd.Layer:setUp tags: zope:layer:sample3.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_error1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample3.sampletests_ntd.TestSomething.test_error1 [ multipart Content-Type: text/x-traceback... traceback 14F\r Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error1 raise TypeError("Can we see errors") TypeError: Can we see errors 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_error2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample3.sampletests_ntd.TestSomething.test_error2 [ multipart Content-Type: text/x-traceback... traceback 13F\r Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_error2 raise TypeError("I hope so") TypeError: I hope so 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_fail1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ failure: sample3.sampletests_ntd.TestSomething.test_fail1 [ multipart Content-Type: text/x-traceback... traceback 1AA\r Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail1 self.assertEqual(1, 2) AssertionError: 1 != 2 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_fail2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ failure: sample3.sampletests_ntd.TestSomething.test_fail2 [ multipart Content-Type: text/x-traceback... traceback 1AA\r Traceback (most recent call last): testrunner-ex/sample3/sampletests_ntd.py", Line NNN, in test_fail2 self.assertEqual(1, 3) AssertionError: 1 != 3 0\r ] time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample3.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_something_else time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample3.sampletests_ntd.TestSomething.test_something_else tags: -zope:layer:sample3.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.Layer:tearDown tags: zope:layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ skip: sample3.sampletests_ntd.Layer:tearDown [ tearDown not supported ] True Note that debugging doesn't work when running tests in a subprocess: >>> sys.argv = [testrunner_script, '--tests-pattern', 'sampletests_ntd$', ... '-D', ] >>> testrunner.run_internal(defaults) time: 2010-02-10 22:41:25.279692Z test: sample1.sampletests_ntd.Layer:setUp tags: zope:layer time: 2010-02-10 22:41:25.279695Z successful: sample1.sampletests_ntd.Layer:setUp tags: zope:layer:sample1.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample1.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample1.sampletests_ntd.TestSomething.test_something tags: -zope:layer:sample1.sampletests_ntd.Layer time: 2010-02-10 22:41:25.310078Z test: sample1.sampletests_ntd.Layer:tearDown tags: zope:layer time: 2010-02-10 22:41:25.310171Z skip: sample1.sampletests_ntd.Layer:tearDown [ tearDown not supported ] test: Running in a subprocess. tags: zope:info_suboptimal successful: Running in a subprocess. time: 2010-02-10 22:41:25.753076Z test: sample2.sampletests_ntd.Layer:setUp tags: zope:layer time: 2010-02-10 22:41:25.753079Z successful: sample2.sampletests_ntd.Layer:setUp tags: zope:layer:sample2.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample2.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample2.sampletests_ntd.TestSomething.test_something tags: -zope:layer:sample2.sampletests_ntd.Layer time: 2010-02-10 22:41:25.779256Z test: sample2.sampletests_ntd.Layer:tearDown tags: zope:layer time: 2010-02-10 22:41:25.779326Z skip: sample2.sampletests_ntd.Layer:tearDown [ tearDown not supported ] test: Running in a subprocess. tags: zope:info_suboptimal successful: Running in a subprocess. time: 2010-02-10 22:41:26.310296Z test: sample3.sampletests_ntd.Layer:setUp tags: zope:layer time: 2010-02-10 22:41:26.310299Z successful: sample3.sampletests_ntd.Layer:setUp tags: zope:layer:sample3.sampletests_ntd.Layer time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_error1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample3.sampletests_ntd.TestSomething.test_error1 [ multipart Content-Type: text/x-traceback... traceback 16A\r Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 42, in test_error1 raise TypeError("Can we see errors") TypeError: Can we see errors 0\r ] test: Can't post-mortem debug when running a layer as a subprocess! tags: zope:error_with_banner successful: Can't post-mortem debug when running a layer as a subprocess! time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_error2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample3.sampletests_ntd.TestSomething.test_error2 [ multipart Content-Type: text/x-traceback... traceback 15A\r Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 45, in test_error2 raise TypeError("I hope so") TypeError: I hope so 0\r ] test: Can't post-mortem debug when running a layer as a subprocess! tags: zope:error_with_banner successful: Can't post-mortem debug when running a layer as a subprocess! time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_fail1 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample3.sampletests_ntd.TestSomething.test_fail1 [ multipart Content-Type: text/x-traceback... traceback 1C5\r Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 48, in test_fail1 self.assertEqual(1, 2) File "/usr/lib/python3.11/unittest.py", line 350, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: 1 != 2 0\r ] test: Can't post-mortem debug when running a layer as a subprocess! tags: zope:error_with_banner successful: Can't post-mortem debug when running a layer as a subprocess! time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_fail2 time: YYYY-MM-DD HH:MM:SS.mmmmmmZ error: sample3.sampletests_ntd.TestSomething.test_fail2 [ multipart Content-Type: text/x-traceback... traceback 1C5\r Traceback (most recent call last): File "/usr/lib/python3.11/unittest.py", line 305, in debug getattr(self, self._testMethodName)() File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample3/sampletests_ntd.py", line 51, in test_fail2 self.assertEqual(1, 3) File "/usr/lib/python3.11/unittest.py", line 350, in failUnlessEqual (msg or '%r != %r' % (first, second)) AssertionError: 1 != 3 0\r ] test: Can't post-mortem debug when running a layer as a subprocess! tags: zope:error_with_banner successful: Can't post-mortem debug when running a layer as a subprocess! time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample3.sampletests_ntd.TestSomething.test_something time: YYYY-MM-DD HH:MM:SS.mmmmmmZ test: sample3.sampletests_ntd.TestSomething.test_something_else time: YYYY-MM-DD HH:MM:SS.mmmmmmZ successful: sample3.sampletests_ntd.TestSomething.test_something_else tags: -zope:layer:sample3.sampletests_ntd.Layer time: 2010-02-10 22:41:26.340878Z test: sample3.sampletests_ntd.Layer:tearDown tags: zope:layer time: 2010-02-10 22:41:26.340945Z skip: sample3.sampletests_ntd.Layer:tearDown [ tearDown not supported ] True Support skipped tests --------------------- >>> directory_with_skipped_tests = os.path.join(this_directory, ... 'testrunner-ex-skip') >>> skip_defaults = [ ... '--path', directory_with_skipped_tests, ... '--tests-pattern', '^sample_skipped_tests$', ... ] >>> sys.argv = ['test'] >>> testrunner.run_internal( ... skip_defaults + ["--subunit", "-t", "TestSkipppedNoLayer"]) time: ... test: zope.testrunner.layer.UnitTests:setUp tags: zope:layer time: ... successful: zope.testrunner.layer.UnitTests:setUp tags: zope:layer:zope.testrunner.layer.UnitTests time: ... test: sample_skipped_tests.TestSkipppedNoLayer.test_skipped skip: sample_skipped_tests.TestSkipppedNoLayer.test_skipped [ I'm a skipped test! ] tags: -zope:layer:zope.testrunner.layer.UnitTests time: ... test: zope.testrunner.layer.UnitTests:tearDown tags: zope:layer time: ... successful: zope.testrunner.layer.UnitTests:tearDown False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-tb-format.rst0000644000076600000240000000000015011314374026274 0ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-test-selection.rst0000644000076600000240000006674315011314374027375 0ustar00m.howitzstaff=============================================== Selecting Tests By Package, Module, and Level =============================================== We've :doc:`already seen ` that we can select tests by layer. There are three other ways we can select tests: by package, by module and test, and by level. Packages ======== We can select tests by package: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test --layer 122 -ssample1 -vv'.split() >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: test_x1 (sample1.sampletests.test122.TestA...) test_y0 (sample1.sampletests.test122.TestA...) test_z0 (sample1.sampletests.test122.TestA...) test_x0 (sample1.sampletests.test122.TestB...) test_y1 (sample1.sampletests.test122.TestB...) test_z0 (sample1.sampletests.test122.TestB...) test_1 (sample1.sampletests.test122.TestNotMuch...) test_2 (sample1.sampletests.test122.TestNotMuch...) test_3 (sample1.sampletests.test122.TestNotMuch...) test_x0 (sample1.sampletests.test122) test_y0 (sample1.sampletests.test122) test_z1 (sample1.sampletests.test122) testrunner-ex/sample1/sampletests/../../sampletestsl.rst Ran 13 tests with 0 failures, 0 errors and 0 skipped in 0.005 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False You can specify multiple packages: >>> sys.argv = 'test -u -vv -ssample1 -ssample2'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_x1 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletestsf.TestA...) test_z0 (sample1.sampletestsf.TestA...) test_x0 (sample1.sampletestsf.TestB...) test_y1 (sample1.sampletestsf.TestB...) test_z0 (sample1.sampletestsf.TestB...) test_1 (sample1.sampletestsf.TestNotMuch...) test_2 (sample1.sampletestsf.TestNotMuch...) test_3 (sample1.sampletestsf.TestNotMuch...) test_x0 (sample1.sampletestsf) test_y0 (sample1.sampletestsf) test_z1 (sample1.sampletestsf) testrunner-ex/sample1/../sampletests.rst test_x1 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sample11.sampletests.TestA...) test_z0 (sample1.sample11.sampletests.TestA...) test_x0 (sample1.sample11.sampletests.TestB...) test_y1 (sample1.sample11.sampletests.TestB...) test_z0 (sample1.sample11.sampletests.TestB...) test_1 (sample1.sample11.sampletests.TestNotMuch...) test_2 (sample1.sample11.sampletests.TestNotMuch...) test_3 (sample1.sample11.sampletests.TestNotMuch...) test_x0 (sample1.sample11.sampletests) test_y0 (sample1.sample11.sampletests) test_z1 (sample1.sample11.sampletests) testrunner-ex/sample1/sample11/../../sampletests.rst test_x1 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample13.sampletests.TestA...) test_z0 (sample1.sample13.sampletests.TestA...) test_x0 (sample1.sample13.sampletests.TestB...) test_y1 (sample1.sample13.sampletests.TestB...) test_z0 (sample1.sample13.sampletests.TestB...) test_1 (sample1.sample13.sampletests.TestNotMuch...) test_2 (sample1.sample13.sampletests.TestNotMuch...) test_3 (sample1.sample13.sampletests.TestNotMuch...) test_x0 (sample1.sample13.sampletests) test_y0 (sample1.sample13.sampletests) test_z1 (sample1.sample13.sampletests) testrunner-ex/sample1/sample13/../../sampletests.rst test_x1 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletests.test1.TestA...) test_z0 (sample1.sampletests.test1.TestA...) test_x0 (sample1.sampletests.test1.TestB...) test_y1 (sample1.sampletests.test1.TestB...) test_z0 (sample1.sampletests.test1.TestB...) test_1 (sample1.sampletests.test1.TestNotMuch...) test_2 (sample1.sampletests.test1.TestNotMuch...) test_3 (sample1.sampletests.test1.TestNotMuch...) test_x0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1) test_z1 (sample1.sampletests.test1) testrunner-ex/sample1/sampletests/../../sampletests.rst test_x1 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sampletests.test_one.TestA...) test_z0 (sample1.sampletests.test_one.TestA...) test_x0 (sample1.sampletests.test_one.TestB...) test_y1 (sample1.sampletests.test_one.TestB...) test_z0 (sample1.sampletests.test_one.TestB...) test_1 (sample1.sampletests.test_one.TestNotMuch...) test_2 (sample1.sampletests.test_one.TestNotMuch...) test_3 (sample1.sampletests.test_one.TestNotMuch...) test_x0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test_one) test_z1 (sample1.sampletests.test_one) testrunner-ex/sample1/sampletests/../../sampletests.rst test_x1 (sample2.sample21.sampletests.TestA...) test_y0 (sample2.sample21.sampletests.TestA...) test_z0 (sample2.sample21.sampletests.TestA...) test_x0 (sample2.sample21.sampletests.TestB...) test_y1 (sample2.sample21.sampletests.TestB...) test_z0 (sample2.sample21.sampletests.TestB...) test_1 (sample2.sample21.sampletests.TestNotMuch...) test_2 (sample2.sample21.sampletests.TestNotMuch...) test_3 (sample2.sample21.sampletests.TestNotMuch...) test_x0 (sample2.sample21.sampletests) test_y0 (sample2.sample21.sampletests) test_z1 (sample2.sample21.sampletests) testrunner-ex/sample2/sample21/../../sampletests.rst test_x1 (sample2.sampletests.test_1.TestA...) test_y0 (sample2.sampletests.test_1.TestA...) test_z0 (sample2.sampletests.test_1.TestA...) test_x0 (sample2.sampletests.test_1.TestB...) test_y1 (sample2.sampletests.test_1.TestB...) test_z0 (sample2.sampletests.test_1.TestB...) test_1 (sample2.sampletests.test_1.TestNotMuch...) test_2 (sample2.sampletests.test_1.TestNotMuch...) test_3 (sample2.sampletests.test_1.TestNotMuch...) test_x0 (sample2.sampletests.test_1) test_y0 (sample2.sampletests.test_1) test_z1 (sample2.sampletests.test_1) testrunner-ex/sample2/sampletests/../../sampletests.rst test_x1 (sample2.sampletests.testone.TestA...) test_y0 (sample2.sampletests.testone.TestA...) test_z0 (sample2.sampletests.testone.TestA...) test_x0 (sample2.sampletests.testone.TestB...) test_y1 (sample2.sampletests.testone.TestB...) test_z0 (sample2.sampletests.testone.TestB...) test_1 (sample2.sampletests.testone.TestNotMuch...) test_2 (sample2.sampletests.testone.TestNotMuch...) test_3 (sample2.sampletests.testone.TestNotMuch...) test_x0 (sample2.sampletests.testone) test_y0 (sample2.sampletests.testone) test_z1 (sample2.sampletests.testone) testrunner-ex/sample2/sampletests/../../sampletests.rst Ran 104 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Directory Names --------------- You can specify directory names instead of packages (useful for tab-completion): >>> subdir = os.path.join(directory_with_tests, 'sample1') >>> sys.argv = ['test', '--layer', '122', '-s', subdir, '-vv'] >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: test_x1 (sample1.sampletests.test122.TestA...) test_y0 (sample1.sampletests.test122.TestA...) test_z0 (sample1.sampletests.test122.TestA...) test_x0 (sample1.sampletests.test122.TestB...) test_y1 (sample1.sampletests.test122.TestB...) test_z0 (sample1.sampletests.test122.TestB...) test_1 (sample1.sampletests.test122.TestNotMuch...) test_2 (sample1.sampletests.test122.TestNotMuch...) test_3 (sample1.sampletests.test122.TestNotMuch...) test_x0 (sample1.sampletests.test122) test_y0 (sample1.sampletests.test122) test_z1 (sample1.sampletests.test122) testrunner-ex/sample1/sampletests/../../sampletestsl.rst Ran 13 tests with 0 failures, 0 errors and 0 skipped in 0.005 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False Modules And Tests ================= We can select by test module name using the --module (-m) option: >>> sys.argv = 'test -u -vv -ssample1 -m_one -mtest1'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_x1 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletests.test1.TestA...) test_z0 (sample1.sampletests.test1.TestA...) test_x0 (sample1.sampletests.test1.TestB...) test_y1 (sample1.sampletests.test1.TestB...) test_z0 (sample1.sampletests.test1.TestB...) test_1 (sample1.sampletests.test1.TestNotMuch...) test_2 (sample1.sampletests.test1.TestNotMuch...) test_3 (sample1.sampletests.test1.TestNotMuch...) test_x0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1) test_z1 (sample1.sampletests.test1) testrunner-ex/sample1/sampletests/../../sampletests.rst test_x1 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sampletests.test_one.TestA...) test_z0 (sample1.sampletests.test_one.TestA...) test_x0 (sample1.sampletests.test_one.TestB...) test_y1 (sample1.sampletests.test_one.TestB...) test_z0 (sample1.sampletests.test_one.TestB...) test_1 (sample1.sampletests.test_one.TestNotMuch...) test_2 (sample1.sampletests.test_one.TestNotMuch...) test_3 (sample1.sampletests.test_one.TestNotMuch...) test_x0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test_one) test_z1 (sample1.sampletests.test_one) testrunner-ex/sample1/sampletests/../../sampletests.rst Ran 26 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False and by test within the module using the --test (-t) option: >>> sys.argv = 'test -u -vv -ssample1 -m_one -mtest1 -t_x0 -t_y0'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_y0 (sample1.sampletests.test1.TestA...) test_x0 (sample1.sampletests.test1.TestB...) test_x0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test_one.TestA...) test_x0 (sample1.sampletests.test_one.TestB...) test_x0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test_one) Ran 8 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False >>> sys.argv = 'test -u -vv -ssample1 -trst'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: testrunner-ex/sample1/../sampletests.rst testrunner-ex/sample1/sample11/../../sampletests.rst testrunner-ex/sample1/sample13/../../sampletests.rst testrunner-ex/sample1/sampletests/../../sampletests.rst testrunner-ex/sample1/sampletests/../../sampletests.rst Ran 5 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Regular Expressions ------------------- The ``--module`` and ``--test`` options take regular expressions. If the regular expressions specified begin with ``!``, then tests that don't match the regular expression are selected: >>> sys.argv = 'test -u -vv -ssample1 -m!sample1[.]sample1'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_x1 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletestsf.TestA...) test_z0 (sample1.sampletestsf.TestA...) test_x0 (sample1.sampletestsf.TestB...) test_y1 (sample1.sampletestsf.TestB...) test_z0 (sample1.sampletestsf.TestB...) test_1 (sample1.sampletestsf.TestNotMuch...) test_2 (sample1.sampletestsf.TestNotMuch...) test_3 (sample1.sampletestsf.TestNotMuch...) test_x0 (sample1.sampletestsf) test_y0 (sample1.sampletestsf) test_z1 (sample1.sampletestsf) testrunner-ex/sample1/../sampletests.rst test_x1 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletests.test1.TestA...) test_z0 (sample1.sampletests.test1.TestA...) test_x0 (sample1.sampletests.test1.TestB...) test_y1 (sample1.sampletests.test1.TestB...) test_z0 (sample1.sampletests.test1.TestB...) test_1 (sample1.sampletests.test1.TestNotMuch...) test_2 (sample1.sampletests.test1.TestNotMuch...) test_3 (sample1.sampletests.test1.TestNotMuch...) test_x0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1) test_z1 (sample1.sampletests.test1) testrunner-ex/sample1/sampletests/../../sampletests.rst test_x1 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sampletests.test_one.TestA...) test_z0 (sample1.sampletests.test_one.TestA...) test_x0 (sample1.sampletests.test_one.TestB...) test_y1 (sample1.sampletests.test_one.TestB...) test_z0 (sample1.sampletests.test_one.TestB...) test_1 (sample1.sampletests.test_one.TestNotMuch...) test_2 (sample1.sampletests.test_one.TestNotMuch...) test_3 (sample1.sampletests.test_one.TestNotMuch...) test_x0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test_one) test_z1 (sample1.sampletests.test_one) testrunner-ex/sample1/sampletests/../../sampletests.rst Ran 39 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Positional Arguments -------------------- Module and test filters can also be given as positional arguments: >>> sys.argv = 'test -u -vv -ssample1 !sample1[.]sample1'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_x1 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletestsf.TestA...) test_z0 (sample1.sampletestsf.TestA...) test_x0 (sample1.sampletestsf.TestB...) test_y1 (sample1.sampletestsf.TestB...) test_z0 (sample1.sampletestsf.TestB...) test_1 (sample1.sampletestsf.TestNotMuch...) test_2 (sample1.sampletestsf.TestNotMuch...) test_3 (sample1.sampletestsf.TestNotMuch...) test_x0 (sample1.sampletestsf) test_y0 (sample1.sampletestsf) test_z1 (sample1.sampletestsf) testrunner-ex/sample1/../sampletests.rst test_x1 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletests.test1.TestA...) test_z0 (sample1.sampletests.test1.TestA...) test_x0 (sample1.sampletests.test1.TestB...) test_y1 (sample1.sampletests.test1.TestB...) test_z0 (sample1.sampletests.test1.TestB...) test_1 (sample1.sampletests.test1.TestNotMuch...) test_2 (sample1.sampletests.test1.TestNotMuch...) test_3 (sample1.sampletests.test1.TestNotMuch...) test_x0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test1) test_z1 (sample1.sampletests.test1) testrunner-ex/sample1/sampletests/../../sampletests.rst test_x1 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sampletests.test_one.TestA...) test_z0 (sample1.sampletests.test_one.TestA...) test_x0 (sample1.sampletests.test_one.TestB...) test_y1 (sample1.sampletests.test_one.TestB...) test_z0 (sample1.sampletests.test_one.TestB...) test_1 (sample1.sampletests.test_one.TestNotMuch...) test_2 (sample1.sampletests.test_one.TestNotMuch...) test_3 (sample1.sampletests.test_one.TestNotMuch...) test_x0 (sample1.sampletests.test_one) test_y0 (sample1.sampletests.test_one) test_z1 (sample1.sampletests.test_one) testrunner-ex/sample1/sampletests/../../sampletests.rst Ran 39 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False >>> sys.argv = 'test -u -vv -ssample1 . rst'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: testrunner-ex/sample1/../sampletests.rst testrunner-ex/sample1/sample11/../../sampletests.rst testrunner-ex/sample1/sample13/../../sampletests.rst testrunner-ex/sample1/sampletests/../../sampletests.rst testrunner-ex/sample1/sampletests/../../sampletests.rst Ran 5 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Levels ====== Sometimes there are tests that you don't want to run by default. For example, you might have tests that take a long time. Tests can have a level attribute. If no level is specified, a level of 1 is assumed and, by default, only tests at level one are run. to run tests at a higher level, use the ``--at-level`` (``-a``) option to specify a higher level. For example, with the following options: >>> sys.argv = 'test -u -vv -t test_y1 -t test_y0'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_y0 (sampletestsf.TestA...) test_y1 (sampletestsf.TestB...) test_y0 (sampletestsf) test_y0 (sample1.sampletestsf.TestA...) test_y1 (sample1.sampletestsf.TestB...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sample11.sampletests.TestA...) test_y1 (sample1.sample11.sampletests.TestB...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y1 (sample1.sample13.sampletests.TestB...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1.TestA...) test_y1 (sample1.sampletests.test1.TestB...) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test_one.TestA...) test_y1 (sample1.sampletests.test_one.TestB...) test_y0 (sample1.sampletests.test_one) test_y0 (sample2.sample21.sampletests.TestA...) test_y1 (sample2.sample21.sampletests.TestB...) test_y0 (sample2.sample21.sampletests) test_y0 (sample2.sampletests.test_1.TestA...) test_y1 (sample2.sampletests.test_1.TestB...) test_y0 (sample2.sampletests.test_1) test_y0 (sample2.sampletests.testone.TestA...) test_y1 (sample2.sampletests.testone.TestB...) test_y0 (sample2.sampletests.testone) test_y0 (sample3.sampletests.TestA...) test_y1 (sample3.sampletests.TestB...) test_y0 (sample3.sampletests) test_y0 (sampletests.test1.TestA...) test_y1 (sampletests.test1.TestB...) test_y0 (sampletests.test1) test_y0 (sampletests.test_one.TestA...) test_y1 (sampletests.test_one.TestB...) test_y0 (sampletests.test_one) Ran 36 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False We get run 36 tests. If we specify a level of 2, we get some additional tests: >>> sys.argv = 'test -u -vv -a 2 -t test_y1 -t test_y0'.split() >>> testrunner.run_internal(defaults) Running tests at level 2 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_y0 (sampletestsf.TestA...) test_y0 (sampletestsf.TestA2...) test_y1 (sampletestsf.TestB...) test_y0 (sampletestsf) test_y0 (sample1.sampletestsf.TestA...) test_y1 (sample1.sampletestsf.TestB...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sample11.sampletests.TestA...) test_y1 (sample1.sample11.sampletests.TestB...) test_y1 (sample1.sample11.sampletests.TestB2...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y1 (sample1.sample13.sampletests.TestB...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1.TestA...) test_y1 (sample1.sampletests.test1.TestB...) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test_one.TestA...) test_y1 (sample1.sampletests.test_one.TestB...) test_y0 (sample1.sampletests.test_one) test_y0 (sample2.sample21.sampletests.TestA...) test_y1 (sample2.sample21.sampletests.TestB...) test_y0 (sample2.sample21.sampletests) test_y0 (sample2.sampletests.test_1.TestA...) test_y1 (sample2.sampletests.test_1.TestB...) test_y0 (sample2.sampletests.test_1) test_y0 (sample2.sampletests.testone.TestA...) test_y1 (sample2.sampletests.testone.TestB...) test_y0 (sample2.sampletests.testone) test_y0 (sample3.sampletests.TestA...) test_y1 (sample3.sampletests.TestB...) test_y0 (sample3.sampletests) test_y0 (sampletests.test1.TestA...) test_y1 (sampletests.test1.TestB...) test_y0 (sampletests.test1) test_y0 (sampletests.test_one.TestA...) test_y1 (sampletests.test_one.TestB...) test_y0 (sampletests.test_one) Ran 38 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False We get run 38 tests. We can specify to run only the level 2 tests using ``--only-level=2``. So we run less tests: >>> sys.argv = 'test -u -vv --only-level=2 -t test_y1 -t test_y0'.split() >>> testrunner.run_internal(defaults) Running tests only at level 2 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_y0 (sampletestsf.TestA2...) test_y1 (sample1.sample11.sampletests.TestB2...) Ran 2 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False We can use the --all option to run tests at all levels: >>> sys.argv = 'test -u -vv --all -t test_y1 -t test_y0'.split() >>> testrunner.run_internal(defaults) Running tests at all levels Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test_y0 (sampletestsf.TestA...) test_y0 (sampletestsf.TestA2...) test_y1 (sampletestsf.TestB...) test_y0 (sampletestsf) test_y0 (sample1.sampletestsf.TestA...) test_y1 (sample1.sampletestsf.TestB...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sample11.sampletests.TestA3...) test_y1 (sample1.sample11.sampletests.TestB...) test_y1 (sample1.sample11.sampletests.TestB2...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y1 (sample1.sample13.sampletests.TestB...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1.TestA...) test_y1 (sample1.sampletests.test1.TestB...) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test_one.TestA...) test_y1 (sample1.sampletests.test_one.TestB...) test_y0 (sample1.sampletests.test_one) test_y0 (sample2.sample21.sampletests.TestA...) test_y1 (sample2.sample21.sampletests.TestB...) test_y0 (sample2.sample21.sampletests) test_y0 (sample2.sampletests.test_1.TestA...) test_y1 (sample2.sampletests.test_1.TestB...) test_y0 (sample2.sampletests.test_1) test_y0 (sample2.sampletests.testone.TestA...) test_y1 (sample2.sampletests.testone.TestB...) test_y0 (sample2.sampletests.testone) test_y0 (sample3.sampletests.TestA...) test_y1 (sample3.sampletests.TestB...) test_y0 (sample3.sampletests) test_y0 (sampletests.test1.TestA...) test_y1 (sampletests.test1.TestB...) test_y0 (sampletests.test1) test_y0 (sampletests.test_one.TestA...) test_y1 (sampletests.test_one.TestB...) test_y0 (sampletests.test_one) Ran 39 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Listing Selected Tests ====================== When you're trying to figure out why the test you want is not matched by the pattern you specified, it is convenient to see which tests match your specifications. >>> sys.argv = 'test --all -m sample1 -t test_y0 --list-tests'.split() >>> testrunner.run_internal(defaults) Listing zope.testrunner.layer.UnitTests tests: test_y0 (sample1.sampletestsf.TestA...) test_y0 (sample1.sampletestsf) test_y0 (sample1.sample11.sampletests.TestA...) test_y0 (sample1.sample11.sampletests.TestA3...) test_y0 (sample1.sample11.sampletests) test_y0 (sample1.sample13.sampletests.TestA...) test_y0 (sample1.sample13.sampletests) test_y0 (sample1.sampletests.test1.TestA...) test_y0 (sample1.sampletests.test1) test_y0 (sample1.sampletests.test_one.TestA...) test_y0 (sample1.sampletests.test_one) Listing samplelayers.Layer11 tests: test_y0 (sample1.sampletests.test11.TestA...) test_y0 (sample1.sampletests.test11) Listing samplelayers.Layer111 tests: test_y0 (sample1.sampletests.test111.TestA...) test_y0 (sample1.sampletests.test111) Listing samplelayers.Layer112 tests: test_y0 (sample1.sampletests.test112.TestA...) test_y0 (sample1.sampletests.test112) Listing samplelayers.Layer12 tests: test_y0 (sample1.sampletests.test12.TestA...) test_y0 (sample1.sampletests.test12) Listing samplelayers.Layer121 tests: test_y0 (sample1.sampletests.test121.TestA...) test_y0 (sample1.sampletests.test121) Listing samplelayers.Layer122 tests: test_y0 (sample1.sampletests.test122.TestA...) test_y0 (sample1.sampletests.test122) False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-verbose.rst0000644000076600000240000001530715011314374026066 0ustar00m.howitzstaffVerbose Output ============== Normally, we just get a summary. We can use the -v option to get increasingly more information. If we use a single --verbose (-v) option, we get a dot printed for each test: >>> import os.path, sys >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex') >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... ] >>> sys.argv = 'test --layer 122 -v'.split() >>> from zope import testrunner >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: .................................. Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.007 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False If there are more than 50 tests, the dots are printed in groups of 50: >>> sys.argv = 'test -uv'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: ................................................................................................................................................................................................ Ran 156 tests with 0 failures, 0 errors and 0 skipped in 0.035 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False If the --verbose (-v) option is used twice, then the name and location of each test is printed as it is run: >>> sys.argv = 'test --layer 122 -vv'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: test_x1 (sample1.sampletests.test122.TestA...) test_y0 (sample1.sampletests.test122.TestA...) test_z0 (sample1.sampletests.test122.TestA...) test_x0 (sample1.sampletests.test122.TestB...) test_y1 (sample1.sampletests.test122.TestB...) test_z0 (sample1.sampletests.test122.TestB...) test_1 (sample1.sampletests.test122.TestNotMuch...) test_2 (sample1.sampletests.test122.TestNotMuch...) test_3 (sample1.sampletests.test122.TestNotMuch...) test_x0 (sample1.sampletests.test122) test_y0 (sample1.sampletests.test122) test_z1 (sample1.sampletests.test122) testrunner-ex/sample1/sampletests/../../sampletestsl.rst test_x1 (sampletests.test122.TestA...) test_y0 (sampletests.test122.TestA...) test_z0 (sampletests.test122.TestA...) test_x0 (sampletests.test122.TestB...) test_y1 (sampletests.test122.TestB...) test_z0 (sampletests.test122.TestB...) test_1 (sampletests.test122.TestNotMuch...) test_2 (sampletests.test122.TestNotMuch...) test_3 (sampletests.test122.TestNotMuch...) test_x0 (sampletests.test122) test_y0 (sampletests.test122) test_z1 (sampletests.test122) testrunner-ex/sampletests/../sampletestsl.rst Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.009 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False if the --verbose (-v) option is used three times, then individual test-execution times are printed: >>> sys.argv = 'test --layer 122 -vvv'.split() >>> testrunner.run_internal(defaults) Running tests at level 1 Running samplelayers.Layer122 tests: Set up samplelayers.Layer1 in 0.000 seconds. Set up samplelayers.Layer12 in 0.000 seconds. Set up samplelayers.Layer122 in 0.000 seconds. Running: test_x1 (sample1.sampletests.test122.TestA...) (0.000 s) test_y0 (sample1.sampletests.test122.TestA...) (0.000 s) test_z0 (sample1.sampletests.test122.TestA...) (0.000 s) test_x0 (sample1.sampletests.test122.TestB...) (0.000 s) test_y1 (sample1.sampletests.test122.TestB...) (0.000 s) test_z0 (sample1.sampletests.test122.TestB...) (0.000 s) test_1 (sample1.sampletests.test122.TestNotMuch...) (0.000 s) test_2 (sample1.sampletests.test122.TestNotMuch...) (0.000 s) test_3 (sample1.sampletests.test122.TestNotMuch...) (0.000 s) test_x0 (sample1.sampletests.test122) (0.001 s) test_y0 (sample1.sampletests.test122) (0.001 s) test_z1 (sample1.sampletests.test122) (0.001 s) testrunner-ex/sample1/sampletests/../../sampletestsl.rst (0.001 s) test_x1 (sampletests.test122.TestA...) (0.000 s) test_y0 (sampletests.test122.TestA...) (0.000 s) test_z0 (sampletests.test122.TestA...) (0.000 s) test_x0 (sampletests.test122.TestB...) (0.000 s) test_y1 (sampletests.test122.TestB...) (0.000 s) test_z0 (sampletests.test122.TestB...) (0.000 s) test_1 (sampletests.test122.TestNotMuch...) (0.000 s) test_2 (sampletests.test122.TestNotMuch...) (0.000 s) test_3 (sampletests.test122.TestNotMuch...) (0.000 s) test_x0 (sampletests.test122) (0.001 s) test_y0 (sampletests.test122) (0.001 s) test_z1 (sampletests.test122) (0.001 s) testrunner-ex/sampletests/../sampletestsl.rst (0.001 s) Ran 26 tests with 0 failures, 0 errors and 0 skipped in 0.009 seconds. Tearing down left over layers: Tear down samplelayers.Layer122 in 0.000 seconds. Tear down samplelayers.Layer12 in 0.000 seconds. Tear down samplelayers.Layer1 in 0.000 seconds. False Quiet output ------------ The --quiet (-q) option cancels all verbose options. It's useful when the default verbosity is non-zero: >>> defaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^sampletestsf?$', ... '-v' ... ] >>> sys.argv = 'test -q -u'.split() >>> testrunner.run_internal(defaults) Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Ran 156 tests with 0 failures, 0 errors and 0 skipped in 0.034 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner-wo-source.rst0000644000076600000240000001051115011314374026334 0ustar00m.howitzstaffRunning Without Source Code =========================== The ``--usecompiled`` option allows running tests in a tree without .py source code, provided compiled .pyc or .pyo files exist (without ``--usecompiled``, .py files are necessary). We have a very simple directory tree, under ``usecompiled/``, to test this. Because we're going to delete its .py files, we want to work in a copy of that: >>> import os.path, shutil, sys, tempfile >>> directory_with_tests = tempfile.mkdtemp() >>> NEWNAME = "unlikely_package_name" >>> src = os.path.join(this_directory, 'testrunner-ex', 'usecompiled') >>> os.path.isdir(src) True >>> dst = os.path.join(directory_with_tests, NEWNAME) >>> os.path.isdir(dst) False Have to use our own copying code, to avoid copying read-only SVN files that can't be deleted later. >>> n = len(src) + 1 >>> for root, dirs, files in os.walk(src): ... dirs[:] = [d for d in dirs if d == "package"] # prune cruft ... os.mkdir(os.path.join(dst, root[n:])) ... for f in files: ... _ = shutil.copy(os.path.join(root, f), ... os.path.join(dst, root[n:], f)) Now run the tests in the copy: >>> from zope import testrunner >>> mydefaults = [ ... '--path', directory_with_tests, ... '--tests-pattern', '^compiletest$', ... '--package', NEWNAME, ... '-vv', ... ] >>> sys.argv = ['test'] >>> testrunner.run_internal(mydefaults) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test1 (unlikely_package_name.compiletest.Test...) test2 (unlikely_package_name.compiletest.Test...) test1 (unlikely_package_name.package.compiletest.Test...) test2 (unlikely_package_name.package.compiletest.Test...) Ran 4 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False If we delete the source files, it's normally a disaster: the test runner doesn't believe any test files, or even packages, exist. Note that we pass ``--keepbytecode`` this time, because otherwise the test runner would delete the compiled Python files too: >>> for root, dirs, files in os.walk(dst): ... for f in files: ... if f.endswith(".py"): ... os.remove(os.path.join(root, f)) >>> testrunner.run_internal(mydefaults, ["test", "--keepbytecode"]) Running tests at level 1 Total: 0 tests, 0 failures, 0 errors and 0 skipped in N.NNN seconds. False Finally, passing ``--usecompiled`` asks the test runner to treat .pyc and .pyo files as adequate replacements for .py files. Note that the output is the same as when running with .py source above. The absence of "removing stale bytecode ..." messages shows that ``--usecompiled`` also implies ``--keepbytecode``: >>> # PEP-3147: pyc files in __pycache__ directories cannot be ... # imported; legacy source-less imports need to use the legacy ... # layout ... for root, dirs, files in os.walk(dst): ... for f in files: ... if f.endswith((".pyc", ".pyo")): ... # "root/f" is "dirname/__pycache__/name.magic.ext" ... dirname = os.path.dirname(os.path.abspath(root)) ... namewmagic, ext = os.path.splitext(os.path.basename(f)) ... newname = os.path.splitext(namewmagic)[0] + ext ... os.rename(os.path.join(root, f), ... os.path.join(dirname, newname)) >>> testrunner.run_internal(mydefaults, ["test", "--usecompiled"]) Running tests at level 1 Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in N.NNN seconds. Running: test1 (unlikely_package_name.compiletest.Test...) test2 (unlikely_package_name.compiletest.Test...) test1 (unlikely_package_name.package.compiletest.Test...) test2 (unlikely_package_name.package.compiletest.Test...) Ran 4 tests with 0 failures, 0 errors and 0 skipped in N.NNN seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in N.NNN seconds. False Remove the temporary directory: >>> shutil.rmtree(directory_with_tests) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/tests/testrunner.rst0000644000076600000240000000546015011314374024422 0ustar00m.howitzstaff========== Overview ========== The testrunner module is used to run automated tests defined using the `unittest` framework. Its primary feature is that it *finds* tests by searching directory trees. It doesn't require the manual concatenation of specific test suites. It is highly customizable and should be usable with any project. In addition to finding and running tests, it provides the following additional features: - :doc:`Test filtering ` using specifications of: * test packages within a larger tree * regular expression patterns for test modules * regular expression patterns for individual tests - Organization of tests into :doc:`levels ` and :doc:`layers ` Sometimes, tests take so long to run that you don't want to run them on every run of the test runner. Tests can be defined at different levels. The test runner can be configured to only run tests at a specific level or below by default. Command-line options can be used to specify a minimum level to use for a specific run, or to run all tests. Individual tests or test suites can specify their level via a ``level`` attribute. where levels are integers increasing from 1. Most tests are unit tests. They don't depend on other facilities, or set up whatever dependencies they have. For larger applications, it's useful to specify common facilities that a large number of tests share. Making each test set up and and tear down these facilities is both ineffecient and inconvenient. For this reason, we've introduced the concept of layers, based on the idea of layered application architectures. Software build for a layer should be able to depend on the facilities of lower layers already being set up. For example, Zope defines a component architecture. Much Zope software depends on that architecture. We should be able to treat the component architecture as a layer that we set up once and reuse. Similarly, Zope application software should be able to depend on the Zope application server without having to set it up in each test. The test runner introduces test layers, which are objects that can set up environments for tests within the layers to use. A layer is set up before running the tests in it. Individual tests or test suites can define a layer by defining a ``layer`` attribute, which is a test layer. - Reporting - :doc:`progress meter ` - summaries of tests run - Analysis of test execution - :doc:`post-mortem debugging ` of test failures - :doc:`memory leaks ` - :doc:`memory management errors ` - :doc:`code coverage ` - :doc:`execution times ` - :doc:`profiling ` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/threadsupport.py0000644000076600000240000000405515011314374023572 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2022 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Thread support beyond ``threading``. ``threading.enumerate`` may or may not know threads started with ``_thread.start_new_thread``. If it knows them, it does not know when they stop. If ``sys._current_frames`` is available, use this to reliable determine the currently running threads. """ import sys import threading current_frames = getattr(sys, "_current_frames", None) if current_frames is None: # pragma: no cover enumerate = threading.enumerate else: def enumerate(): """return sequence of proxies for the currently running threads.""" running = set(current_frames()) th_known = {t.ident: t for t in threading.enumerate()} return [ThreadProxy(th_known[i] if i in th_known else DummyThread(i)) for i in running] class ThreadProxy: """auxiliary class to provide ident based ``__eq__``.""" def __init__(self, thread): self.thread = thread def __eq__(self, other): return self.thread.ident == other.thread.ident def __repr__(self): return repr(self.thread) def __getattr__(self, k): return getattr(self.thread, k) class DummyThread: """auxiliary to represent a thread unknown to ``threading``.""" def __init__(self, ident): self.ident = ident self.name = "Dummy-%s" % ident def __repr__(self): return "DummyThread %s, started, daemon" % self.ident def is_alive(self): return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/src/zope/testrunner/util.py0000644000076600000240000000152015011314374021635 0ustar00m.howitzstaff############################################################################## # # Copyright (c) 2022 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Some general auxiliary functions. """ import platform import sys is_jython = sys.platform.startswith('java') is_pypy = platform.python_implementation() == "PyPy" uses_refcounts = not (is_jython or is_pypy) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1747294461.8761122 zope_testrunner-7.3/src/zope.testrunner.egg-info/0000755000076600000240000000000015011314376022003 5ustar00m.howitzstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/PKG-INFO0000644000076600000240000006026315011314375023106 0ustar00m.howitzstaffMetadata-Version: 2.1 Name: zope.testrunner Version: 7.3 Summary: Zope testrunner script. Home-page: https://github.com/zopefoundation/zope.testrunner Author: Zope Foundation and Contributors Author-email: zope-dev@zope.dev License: ZPL-2.1 Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Framework :: Zope :: 3 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Testing Requires-Python: >=3.9 License-File: LICENSE.md Requires-Dist: setuptools Requires-Dist: zope.exceptions Requires-Dist: zope.interface Provides-Extra: test Requires-Dist: zope.testing; extra == "test" Provides-Extra: subunit Requires-Dist: python-subunit>=1.4.3; extra == "subunit" Requires-Dist: testtools>=0.9.30; extra == "subunit" Provides-Extra: docs Requires-Dist: Sphinx; extra == "docs" Requires-Dist: sphinxcontrib-programoutput; extra == "docs" ================= zope.testrunner ================= .. image:: https://img.shields.io/pypi/v/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.testrunner.svg :target: https://pypi.org/project/zope.testrunner/ :alt: Supported Python versions .. image:: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.testrunner/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.testrunner/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.testrunner?branch=master .. image:: https://readthedocs.org/projects/zopetestrunner/badge/?version=latest :target: https://zopetestrunner.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. contents:: This package provides a flexible test runner with layer support. Detailed documentation is hosted at https://zopetestrunner.readthedocs.io ======================= Using zope.testrunner ======================= .. IMPORTANT: This document is included in the long_description for rendering on PyPI, so it cannot use Sphinx-only features. Installation ============ Buildout-based projects ----------------------- zope.testrunner is often used for projects that use buildout_:: [buildout] develop = . parts = ... test ... [test] recipe = zc.recipe.testrunner eggs = mypackage The usual buildout process :: python bootstrap.py bin/buildout creates a ``bin/test`` script that will run the tests for *mypackage*. .. tip:: zc.recipe.testrunner_ takes care to specify the right ``--test-path`` option in the generated script. You can add other options (such as ``--tests-pattern``) too; check zc.recipe.testrunner_'s documentation for details. Virtualenv-based projects ------------------------- ``pip install zope.testrunner`` and you'll get a ``zope-testrunner`` script. Run your tests with :: zope-testrunner --test-path=path/to/your/source/tree Your source code needs to be available for the testrunner to import, so you need to run ``python setup.py install`` or ``pip install -e .`` into the same virtualenv_. Some useful command-line options to get you started =================================================== -p show a progress indicator -v increase verbosity -c colorize the output -t test specify test names (one or more regexes) -m module specify test modules (one or more regexes) -s package specify test packages (one or more regexes) --list-tests show names of tests instead of running them -x stop on first error or failure -D, --pdb enable post-mortem debugging of test failures --xml path generate XML reports to be written at the given path --help show *all* command-line options (there are many more!) For example :: bin/test -pvc -m test_foo -t TestBar runs all TestBar tests from a module called test_foo.py. Writing tests ============= ``zope.testrunner`` expects to find your tests inside your package directory, in a subpackage or module named ``tests``. Test modules in a test subpackage should be named ``test*.py``. .. tip:: You can change these assumptions with ``--tests-pattern`` and ``--test-file-pattern`` test runner options. Tests themselves should be classes inheriting from ``unittest.TestCase``, and if you wish to use doctests, please tell the test runner where to find them and what options to use for them in by supplying a function named ``test_suite``. Example:: import unittest import doctest class TestArithmetic(unittest.TestCase): def test_two_plus_two(self): self.assertEqual(2 + 2, 4) def doctest_string_formatting(): """Test Python string formatting >>> print('{} + {}'.format(2, 2)) 2 + 2 """ def test_suite(): return unittest.TestSuite([ unittest.defaultTestLoader.loadTestsFromName(__name__), doctest.DocTestSuite(), doctest.DocFileSuite('../README.txt', optionflags=doctest.ELLIPSIS), ]) Test grouping ============= In addition to per-package and per-module filtering, zope.testrunner has other mechanisms for grouping tests: * **layers** allow you to have shared setup/teardown code to be used by a group of tests, that is executed only once, and not for each test. Layers are orthogonal to the usual package/module structure and are specified by setting the ``layer`` attribute on test suites. * **levels** allow you to group slow-running tests and not run them by default. They're specified by setting the ``level`` attribute on test suites to an int. Other features ============== zope.testrunner can profile your tests, measure test coverage, check for memory leaks, integrate with subunit_, shuffle the test execution order, and run multiple tests in parallel. .. _buildout: https://buildout.readthedocs.io .. _virtualenv: https://virtualenv.pypa.io/ .. _zc.recipe.testrunner: https://pypi.python.org/pypi/zc.recipe.testrunner .. _subunit: https://pypi.python.org/pypi/python-subunit =========================== zope.testrunner Changelog =========================== 7.3 (2025-05-15) ================ - Improve ``@unittest.expectedFailure`` support, especially regarding post-mortem debugger. (`#196 `_) 7.2 (2025-03-06) ================ - Re-add a single import of ``pkg_resources`` to avoid other import issues in mixed pip/buildout environments. (`#194 `_) 7.1 (2025-03-05) ================ - Replace ``pkg_resources`` with ``importlib.metadata``. 7.0 (2025-02-12) ================ Backwards-incompatible changes ------------------------------ - Remove ``setup.py ftest`` command. (`#178 `_) - Remove ``zope.testrunner.eggsupport``. It is no longer usable as of ``setuptools`` 72.0.0. (`#185 `_) 6.7 (2025-02-07) ================ - Drop support for Python 3.8. - Add option ``--only-level=level`` to run tests only at the specified level. (`#188 `_) 6.6.1 (2024-12-13) ================== - Make signatures in ``tb_format`` Python 3.12+ compatible (`#186 `_) 6.6 (2024-10-16) ================ - Make tests compatible with Python 3.13 + add support for that version. (`#181 `_) - Drop support for Python 3.7. 6.5 (2024-08-06) ================ - Remove setuptools fossils. - ``unittest.TestCase.subTest`` support (`#91 `_). - remove support for ``setup.py``'s ``test`` command. Support for this command has been dropped by modern ``setuptools`` versions and correspondingly has been removed from most ``zopefoundation`` packages; ``zope.testrunner`` now follows. - ``setup.py``'s ``ftest`` command is now only supported when the used ``setuptools`` version still supports ``test``. 6.4 (2024-02-27) ================ - Add PEP 420 support (implicit namespaces). (`#160 `_) 6.3.1 (2024-02-12) ================== - Fix XML tests when running in distribution resp. separately. (`#163 `_) 6.3 (2024-02-07) ================ - Exit cleanly when using the test runner ``--version`` argument. (`#102 `_) - Add new ``--xml `` option to write JUnit-like XML reports. Code comes from ``collective.xmltestreport``, but be aware that here ``--xml`` is not a boolean, but expects a path! (`#148 `_). - Add support for Python 3.13a3. 6.2.1 (2023-12-22) ================== - Work around Python 3.12.1+ no longer calling ``startTest`` for skipped tests (`#157 `_). 6.2 (2023-11-08) ================ - Add support for Python 3.12. - Update code and tests to ``python-subunit >= 1.4.3`` thus requiring at least this version. 6.1 (2023-08-26) ================ - Add preliminary support for Python 3.12b4. (`#149 `_) 6.0 (2023-03-28) ================ - Drop support for Python 2.7, 3.5, 3.6. 5.6 (2022-12-09) ================ - Add support for Python 3.11. - Inline a small part of ``random.Random.shuffle`` which was deprecated in Python 3.9 and removed in 3.11 (`#119 `_). - Don't trigger post mortem debugger for skipped tests. ( `#141 `_). 5.5.1 (2022-09-07) ================== - Fix: let ``--at-level=level`` with ``level <= 0`` run the tests at all levels (rather than at no level) `#138 `_. 5.5 (2022-06-24) ================ - Use ``sys._current_frames`` (rather than ``threading.enumerate``) as base for new thread detection, fixes `#130 `_. - New option ``--gc-after-test``. It calls for a garbage collection after each test and can be used to track down ``ResourceWarning``s and cyclic garbage. With ``rv = gc.collect()``, ``!`` is output on verbosity level 1 when ``rv`` is non zero (i.e. when cyclic structures have been released), ``[``*rv*``]`` on higher verbosity levels and a detailed cyclic garbage analysis on verbosity level 4+. For details, see `#133 `_. - Allow the filename for the logging configuration to be specified via the envvar ``ZOPE_TESTRUNNER_LOG_INI``. If not defined, the configuration continues to be locked for in file ``log.ini`` of the current working directory. Remember the logging configuration file in envvar ``ZOPE_TESTRUNNER_LOG_INI`` to allow spawned child processes to recreate the logging configuration. For details, see `#134 `_. 5.4.0 (2021-11-19) ================== - Improve ``--help`` documentation for ``--package-path`` option (`#121 `_). - Do not disable existing loggers during logsupport initialization (`#120 `_). - Fix tests with testtools >= 2.5.0 (`#125 `_). - Add support for Python 3.10. 5.3.0 (2021-03-17) ================== - Add support for Python 3.9. - Fix `package init file missing` warning (`#112 `_). - Make standard streams provide a `buffer` attribute on Python 3 when using `--buffer` or testing under subunit. 5.2 (2020-06-29) ================ - Add support for Python 3.8. - When a layer is run in a subprocess, read its stderr in a thread to avoid a deadlock if its stderr output (containing failing and erroring test IDs) overflows the capacity of a pipe (`#105 `_). 5.1 (2019-10-19) ================ - Recover more gracefully when layer setUp or tearDown fails, producing useful subunit output. - Prevent a spurious warning from the ``--require-unique`` option if the ``--module`` option was not used. - Add optional buffering of standard output and standard error during tests, requested via the ``--buffer`` option or enabled by default for subunit. - Fix incorrect failure counts in per-layer summary output, broken in 4.0.1. 5.0 (2019-03-19) ================ - Fix test failures and deprecation warnings occurring when using Python 3.8a1. (`#89 `_) - Drop support for Python 3.4. 4.9.2 (2018-11-24) ================== - Fix ``TypeError: a bytes-like object is required, not 'str'`` running tests in parallel on Python 3. See `issue 80 `_. 4.9.1 (2018-11-21) ================== - Fix AssertionError in _DummyThread.isAlive on Python 3 (`#81 `_). 4.9 (2018-10-05) ================ - Drop support for Python 3.3. - Add support for Python 3.7. - Enable test coverage reporting on coveralls.io and in tox.ini. - Host documentation at https://zopetestrunner.readthedocs.io - Remove untested support for the ``--pychecker`` option. See `issue 63 `_. - Update the command line interface to use ``argparse`` instead of ``optparse``. See `issue 61 `_. - Use ipdb instead of pdb for post-mortem debugging if available (`#10 `_). - Add a --require-unique option to check for duplicate test IDs. See `LP #682771 `_. - Reintroduce optional support for ``subunit``, now with support for both version 1 and version 2 of its protocol. - Handle string in exception values when formatting chained exceptions. (`#74 `_) 4.8.1 (2017-11-12) ================== - Enable ``DeprecationWarning`` earlier, when discovering test modules. This lets warnings that are raised on import (such as those produced by ``zope.deprecation.moved``) be reported. See `issue 57 `_. 4.8.0 (2017-11-10) ================== - Automatically enable ``DeprecationWarning`` when running tests. This is recommended by the Python core developers and matches the behaviour of the ``unittest`` module. This can be overridden with Python command-line options (``-W``) or environment variables (``PYTHONWARNINGS``). See `issue 54 `_. 4.7.0 (2017-05-30) ================== - Drop all support for ``subunit``. 4.6.0 (2016-12-28) ================== - Make the ``subunit`` support purely optional: applications which have been getting the dependencies via ``zope.testrunner`` should either add ``zope.testrunner[subunit]`` to their ``install_requires`` or else depend directly on ``python-subunit``. - New option ``--ignore-new-thread=`` to suppress "New thread(s)" warnings. - Support Python 3.6. 4.5.1 (2016-06-20) ================== - Fixed: Using the ``-j`` option to run tests in multiple processes caused tests that used the ``multiprocessing`` package to hang (because the testrunner replaced ``sys.stdin`` with an unclosable object). - Drop conditional dependency on ``unittest2`` (redundant after dropping support for Python 2.6). 4.5.0 (2016-05-02) ================== - Stop tests for all layers when test fails/errors when started with -x/--stop-on-error (`#37 `_). - Drop support for Python 2.6 and 3.2. 4.4.10 (2015-11-10) =================== - Add support for Python 3.5 (`#31 `_). - Insert extra paths (from ``--path``) to the front of sys.argv (`#32 `_). 4.4.9 (2015-05-21) ================== - When using ``-j``, parallelize all the tests, including the first test layer (`#28 `_). 4.4.8 (2015-05-01) ================== - Support skipped tests in subunit output (`#25 `_). - More efficient test filtering (`#26 `_). 4.4.7 (2015-04-02) ================== - Work around a bug in PyPy3's curses module (`#24 `_). 4.4.6 (2015-01-21) ================== - Restore support for instance-based test layers that regressed in 4.4.5 (`#20 `_). 4.4.5 (2015-01-06) ================== - Sort related layers close to each other to reduce the number of unnecessary teardowns (fixes `#14 `_). - Run the unit test layer first (fixes `LP #497871 `__). 4.4.4 (2014-12-27) ================== - When looking for the right location of test code, start with longest location paths first. This fixes problems with nested code locations. 4.4.3 (2014-03-19) ================== - Added support for Python 3.4. 4.4.2 (2014-02-22) ================== - Drop support for Python 3.1. - Fix post-mortem debugging when a non-printable exception happens (https://github.com/zopefoundation/zope.testrunner/issues/8). 4.4.1 (2013-07-10) ================== - Updated ``boostrap.py`` to version 2.2. - Fix nondeterministic test failures on Python 3.3 - Tear down layers after ``post_mortem`` debugging is finished. - Fix tests that write to source directory, it might be read-only. 4.4.0 (2013-06-06) ================== - Fix tests selection when the negative "!" pattern is used several times (LP #1160965) - Moved tests into a 'tests' subpackage. - Made ``python -m zope.testrunner`` work again. - Support 'skip' feature of unittest2 (which became the new unittest in Python 2.7). - Better diagnostics when communication with subprocess fails (https://github.com/zopefoundation/zope.testrunner/issues/5). - Do not break subprocess execution when the test suite changes the working directory (https://github.com/zopefoundation/zope.testrunner/issues/6). - Count test module import errors as errors (LP #1026576). 4.3.3 (2013-03-03) ================== - Running layers in sub-processes did not use to work when run via ``python setup.py ftest`` since it tried to run setup.py with all the command line options. It now detects ``setup.py`` runs and we run the test runner directly. 4.3.2 (2013-03-03) ================== - Fix ``SkipLayers`` class in cases where the distribution specifies a ``test_suite`` value. 4.3.1 (2013-03-02) ================== - Fixed a bug in the `ftest` command and added a test. - Fixed a trivial test failure with Python 3 of the previous release. 4.3.0 (2013-03-02) ================== - Expose `ftest` distutils command via an entry point. - Added tests for ``zope.testrunner.eggsupport``. 4.2.0 (2013-02-12) ================== - Dropped use of 2to3, rewrote source code to be compatible with all Python versions. Introduced a dependency on `six`_. 4.1.1 (2013-02-08) ================== - Dropped use of zope.fixers (LP: #1118877). - Fixed tox test error reporting; fixed tests on Pythons 2.6, 3.1, 3.2, 3.3 and PyPy 1.9. - Fix --shuffle ordering on Python 3.2 to be the same as it was on older Python versions. - Fix --shuffle nondeterminism when multiple test layers are present. Note: this will likely change the order of tests for the same --shuffle-seed. - New option: --profile-directory. Use it in the test suite so that tests executed by detox in parallel don't conflict. - Use a temporary coverage directory in the test suite so that tests executed by detox in parallel don't conflict. - Fix --post-mortem (aka -D, --pdb) when a test module cannot be imported or is invalid (LP #1119363). 4.1.0 (2013-02-07) ================== - Replaced deprecated ``zope.interface.implements`` usage with equivalent ``zope.interface.implementer`` decorator. - Dropped support for Python 2.4 and 2.5. - Made StartUpFailure compatible with unittest.TextTestRunner() (LP #1118344). 4.0.4 (2011-10-25) ================== - Work around sporadic timing-related issues in the subprocess buffering tests. Thanks to Jonathan Ballet for the patch! 4.0.3 (2011-03-17) ================== - Added back support for Python <= 2.6 which was broken in 4.0.2. 4.0.2 (2011-03-16) ================== - Added back Python 3 support which was broken in 4.0.1. - Fixed `Unexpected success`_ support by implementing the whole concept. - Added support for the new __pycache__ directories in Python 3.2. 4.0.1 (2011-02-21) ================== - LP #719369: An `Unexpected success`_ (concept introduced in Python 2.7) is no longer handled as success but as failure. This is a workaround. The whole unexpected success concept might be implemented later. .. _`Unexpected success`: http://www.voidspace.org.uk/python/articles/unittest2.shtml#more-skipping 4.0.0 (2010-10-19) ================== - Show more information about layers whose setup fails (LP #638153). 4.0.0b5 (2010-07-20) ==================== - Update fix for LP #221151 to a spelling compatible with Python 2.4. - Timestamps are now always included in subunit output (r114849). - LP #591309: fix a crash when subunit reports test failures containing UTF8-encoded data. 4.0.0b4 (2010-06-23) ==================== - Package as a zipfile to work around Python 2.4 distutils bug (no feature changes or bugfixes in ``zope.testrunner`` itself). 4.0.0b3 (2010-06-16) ==================== - LP #221151: keep ``unittest.TestCase.shortDescription`` happy by supplying a ``_testMethodDoc`` attribute. - LP #595052: keep the distribution installable under Python 2.4: its distutils appears to munge the empty ``__init__.py`` file in the ``foo.bar`` egg used for testing into a directory. - LP #580083: fix the ``bin/test`` script to run only tests from ``zope.testrunner``. - LP #579019: When layers were run in parallel, their tearDown was not called. Additionally, the first layer which was run in the main thread did not have its tearDown called either. 4.0.0b2 (2010-05-03) ==================== - Having 'sampletests' in the MANIFEST.in gave warnings, but doesn't actually seem to include any more files, so I removed it. - Moved zope.testing.exceptions to zope.testrunner.exceptions. Now zope.testrunner no longer requires zope.testing except for when running its own tests. 4.0.0b1 (2010-04-29) ==================== - Initial release of the testrunner from zope.testrunner as its own module. (Previously it was part of zope.testing.) .. _six: http://pypi.python.org/pypi/six ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/SOURCES.txt0000644000076600000240000002723515011314375023677 0ustar00m.howitzstaff.pre-commit-config.yaml .readthedocs.yaml CHANGES.rst CONTRIBUTING.md COPYRIGHT.rst DEVELOPING.rst LICENSE.md MANIFEST.in README.rst buildout.cfg pyproject.toml setup.cfg setup.py tox.ini docs/api.rst docs/changelog.rst docs/cli.rst docs/conf.py docs/getting-started.rst docs/index.rst docs/requirements.txt docs/testrunner-arguments.rst docs/testrunner-colors.rst docs/testrunner-coverage.rst docs/testrunner-debugging.rst docs/testrunner-discovery.rst docs/testrunner-edge-cases.rst docs/testrunner-errors.rst docs/testrunner-gc.rst docs/testrunner-knit.rst docs/testrunner-layers-api.rst docs/testrunner-layers-instances.rst docs/testrunner-layers-ntd.rst docs/testrunner-layers.rst docs/testrunner-leaks.rst docs/testrunner-new-threads.rst docs/testrunner-profiling.rst docs/testrunner-progress.rst docs/testrunner-repeat.rst docs/testrunner-shuffle.rst docs/testrunner-simple.rst docs/testrunner-test-selection.rst docs/testrunner-verbose.rst docs/testrunner-wo-source.rst docs/testrunner.rst src/zope/__init__.py src/zope.testrunner.egg-info/PKG-INFO src/zope.testrunner.egg-info/SOURCES.txt src/zope.testrunner.egg-info/dependency_links.txt src/zope.testrunner.egg-info/entry_points.txt src/zope.testrunner.egg-info/namespace_packages.txt src/zope.testrunner.egg-info/not-zip-safe src/zope.testrunner.egg-info/requires.txt src/zope.testrunner.egg-info/top_level.txt src/zope/testrunner/__init__.py src/zope/testrunner/__main__.py src/zope/testrunner/_doctest.py src/zope/testrunner/coverage.py src/zope/testrunner/debug.py src/zope/testrunner/digraph.py src/zope/testrunner/exceptions.py src/zope/testrunner/feature.py src/zope/testrunner/filter.py src/zope/testrunner/find.py src/zope/testrunner/formatter.py src/zope/testrunner/garbagecollection.py src/zope/testrunner/interfaces.py src/zope/testrunner/layer.py src/zope/testrunner/listing.py src/zope/testrunner/logsupport.py src/zope/testrunner/options.py src/zope/testrunner/process.py src/zope/testrunner/profiling.py src/zope/testrunner/refcount.py src/zope/testrunner/runner.py src/zope/testrunner/selftest.py src/zope/testrunner/shuffle.py src/zope/testrunner/statistics.py src/zope/testrunner/tb_format.py src/zope/testrunner/threadsupport.py src/zope/testrunner/util.py src/zope/testrunner/tests/__init__.py src/zope/testrunner/tests/test_digraph.py src/zope/testrunner/tests/test_doctest.py src/zope/testrunner/tests/test_filter.py src/zope/testrunner/tests/test_find.py src/zope/testrunner/tests/test_logsupport.py src/zope/testrunner/tests/test_runner.py src/zope/testrunner/tests/test_subunit.py src/zope/testrunner/tests/test_threadsupport.py src/zope/testrunner/tests/test_xml_output.py src/zope/testrunner/tests/testrunner-arguments.rst src/zope/testrunner/tests/testrunner-colors.rst src/zope/testrunner/tests/testrunner-coverage-win32.rst src/zope/testrunner/tests/testrunner-coverage.rst src/zope/testrunner/tests/testrunner-debugging-import-failure.rst src/zope/testrunner/tests/testrunner-debugging-layer-setup.rst src/zope/testrunner/tests/testrunner-debugging-nonprintable-exc.rst src/zope/testrunner/tests/testrunner-debugging.rst src/zope/testrunner/tests/testrunner-discovery.rst src/zope/testrunner/tests/testrunner-edge-cases.rst src/zope/testrunner/tests/testrunner-errors.rst src/zope/testrunner/tests/testrunner-expected-failures.rst src/zope/testrunner/tests/testrunner-gc-after-test.rst src/zope/testrunner/tests/testrunner-gc.rst src/zope/testrunner/tests/testrunner-knit.rst src/zope/testrunner/tests/testrunner-layers-api.rst src/zope/testrunner/tests/testrunner-layers-buff.rst src/zope/testrunner/tests/testrunner-layers-cantfind.rst src/zope/testrunner/tests/testrunner-layers-cwd.rst src/zope/testrunner/tests/testrunner-layers-instances.rst src/zope/testrunner/tests/testrunner-layers-ntd.rst src/zope/testrunner/tests/testrunner-layers-topological-sort.rst src/zope/testrunner/tests/testrunner-layers.rst src/zope/testrunner/tests/testrunner-leaks-err.rst src/zope/testrunner/tests/testrunner-leaks.rst src/zope/testrunner/tests/testrunner-nestedcode.rst src/zope/testrunner/tests/testrunner-new-threads.rst src/zope/testrunner/tests/testrunner-profiling-cprofiler.rst src/zope/testrunner/tests/testrunner-profiling.rst src/zope/testrunner/tests/testrunner-progress.rst src/zope/testrunner/tests/testrunner-repeat.rst src/zope/testrunner/tests/testrunner-report-skipped.rst src/zope/testrunner/tests/testrunner-shuffle.rst src/zope/testrunner/tests/testrunner-simple.rst src/zope/testrunner/tests/testrunner-stops-when-stop-on-error.rst src/zope/testrunner/tests/testrunner-subprocess-errors.rst src/zope/testrunner/tests/testrunner-subtest.rst src/zope/testrunner/tests/testrunner-subunit-err.rst src/zope/testrunner/tests/testrunner-subunit-leaks.rst src/zope/testrunner/tests/testrunner-subunit-v2.rst src/zope/testrunner/tests/testrunner-subunit.rst src/zope/testrunner/tests/testrunner-tb-format.rst src/zope/testrunner/tests/testrunner-test-selection.rst src/zope/testrunner/tests/testrunner-verbose.rst src/zope/testrunner/tests/testrunner-wo-source.rst src/zope/testrunner/tests/testrunner.rst src/zope/testrunner/tests/logsupport/log.ini src/zope/testrunner/tests/testrunner-ex/README.rst src/zope/testrunner/tests/testrunner-ex/brokenlayer.py src/zope/testrunner/tests/testrunner-ex/gc-after-test.py src/zope/testrunner/tests/testrunner-ex/gc0.py src/zope/testrunner/tests/testrunner-ex/gc1.py src/zope/testrunner/tests/testrunner-ex/gcset.py src/zope/testrunner/tests/testrunner-ex/gcstats.py src/zope/testrunner/tests/testrunner-ex/leak.py src/zope/testrunner/tests/testrunner-ex/new_threads.py src/zope/testrunner/tests/testrunner-ex/pledge.py src/zope/testrunner/tests/testrunner-ex/samplelayers.py src/zope/testrunner/tests/testrunner-ex/sampletests.rst src/zope/testrunner/tests/testrunner-ex/sampletests_buffering.py src/zope/testrunner/tests/testrunner-ex/sampletests_many.py src/zope/testrunner/tests/testrunner-ex/sampletestsf.py src/zope/testrunner/tests/testrunner-ex/sampletestsl.rst src/zope/testrunner/tests/testrunner-ex/subtest.py src/zope/testrunner/tests/testrunner-ex/unicode.py src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/test.py src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/__init__.py src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/bar/__init__.py src/zope/testrunner/tests/testrunner-ex-251759/eggs/foo.bar-1.2-py2.5.egg/foo/bar/tests.py src/zope/testrunner/tests/testrunner-ex-37/layers.py src/zope/testrunner/tests/testrunner-ex-37/stop_on_error.py src/zope/testrunner/tests/testrunner-ex-37/stop_on_failure.py src/zope/testrunner/tests/testrunner-ex-6/cwdtests.py src/zope/testrunner/tests/testrunner-ex-expectedFailure/sample_expected_failure_tests.py src/zope/testrunner/tests/testrunner-ex-pp-lib/sample4/__init__.py src/zope/testrunner/tests/testrunner-ex-pp-lib/sample4/products/__init__.py src/zope/testrunner/tests/testrunner-ex-pp-products/__init__.py src/zope/testrunner/tests/testrunner-ex-pp-products/sampletests.py src/zope/testrunner/tests/testrunner-ex-pp-products/more/__init__.py src/zope/testrunner/tests/testrunner-ex-pp-products/more/sampletests.py src/zope/testrunner/tests/testrunner-ex-skip/sample_skipped_tests.py src/zope/testrunner/tests/testrunner-ex/sample1/__init__.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_discover.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_discover_notests.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_none_suite.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_none_test.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_ntd.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests_ntds.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletestsf.py src/zope/testrunner/tests/testrunner-ex/sample1/sample11/__init__.py src/zope/testrunner/tests/testrunner-ex/sample1/sample11/sampletests.py src/zope/testrunner/tests/testrunner-ex/sample1/sample12/__init__.py src/zope/testrunner/tests/testrunner-ex/sample1/sample13/__init__.py src/zope/testrunner/tests/testrunner-ex/sample1/sample13/sampletests.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/__init__.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test1.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test11.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test111.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test112.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test12.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test121.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test122.py src/zope/testrunner/tests/testrunner-ex/sample1/sampletests/test_one.py src/zope/testrunner/tests/testrunner-ex/sample2/__init__.py src/zope/testrunner/tests/testrunner-ex/sample2/badsyntax.py src/zope/testrunner/tests/testrunner-ex/sample2/e.rst src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_1.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_e.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_f.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_ntd.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests_ntds.py src/zope/testrunner/tests/testrunner-ex/sample2/stderrtest.py src/zope/testrunner/tests/testrunner-ex/sample2/stdstreamstest.py src/zope/testrunner/tests/testrunner-ex/sample2/do-not-enter/sampletests.py src/zope/testrunner/tests/testrunner-ex/sample2/sample21/__init__.py src/zope/testrunner/tests/testrunner-ex/sample2/sample21/sampletests.py src/zope/testrunner/tests/testrunner-ex/sample2/sample21/sampletests_i.py src/zope/testrunner/tests/testrunner-ex/sample2/sample22/__init__.py src/zope/testrunner/tests/testrunner-ex/sample2/sample22/sampletests_i.py src/zope/testrunner/tests/testrunner-ex/sample2/sample23/__init__.py src/zope/testrunner/tests/testrunner-ex/sample2/sample23/sampletests_i.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/__init__.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/test_1.py src/zope/testrunner/tests/testrunner-ex/sample2/sampletests/testone.py src/zope/testrunner/tests/testrunner-ex/sample3/__init__.py src/zope/testrunner/tests/testrunner-ex/sample3/post_mortem5.rst src/zope/testrunner/tests/testrunner-ex/sample3/post_mortem6.rst src/zope/testrunner/tests/testrunner-ex/sample3/post_mortem_failure.rst src/zope/testrunner/tests/testrunner-ex/sample3/sampletests.py src/zope/testrunner/tests/testrunner-ex/sample3/sampletests_d.py src/zope/testrunner/tests/testrunner-ex/sample3/sampletests_ntd.py src/zope/testrunner/tests/testrunner-ex/sample3/set_trace5.rst src/zope/testrunner/tests/testrunner-ex/sample3/set_trace6.rst src/zope/testrunner/tests/testrunner-ex/sample3/sample31/__init__.py src/zope/testrunner/tests/testrunner-ex/sample3/sample32/__init__.py src/zope/testrunner/tests/testrunner-ex/sample3/sample33/__init__.py src/zope/testrunner/tests/testrunner-ex/sampletests/__init__.py src/zope/testrunner/tests/testrunner-ex/sampletests/test1.py src/zope/testrunner/tests/testrunner-ex/sampletests/test11.py src/zope/testrunner/tests/testrunner-ex/sampletests/test111.py src/zope/testrunner/tests/testrunner-ex/sampletests/test112.py src/zope/testrunner/tests/testrunner-ex/sampletests/test12.py src/zope/testrunner/tests/testrunner-ex/sampletests/test121.py src/zope/testrunner/tests/testrunner-ex/sampletests/test122.py src/zope/testrunner/tests/testrunner-ex/sampletests/test_one.py src/zope/testrunner/tests/testrunner-ex/usecompiled/README.rst src/zope/testrunner/tests/testrunner-ex/usecompiled/__init__.py src/zope/testrunner/tests/testrunner-ex/usecompiled/compiletest.py src/zope/testrunner/tests/testrunner-ex/usecompiled/package/__init__.py src/zope/testrunner/tests/testrunner-ex/usecompiled/package/compiletest.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/dependency_links.txt0000644000076600000240000000000115011314375026050 0ustar00m.howitzstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/entry_points.txt0000644000076600000240000000007015011314375025275 0ustar00m.howitzstaff[console_scripts] zope-testrunner = zope.testrunner:run ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/namespace_packages.txt0000644000076600000240000000000515011314375026330 0ustar00m.howitzstaffzope ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/not-zip-safe0000644000076600000240000000000115011314375024230 0ustar00m.howitzstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/requires.txt0000644000076600000240000000023515011314375024402 0ustar00m.howitzstaffsetuptools zope.exceptions zope.interface [docs] Sphinx sphinxcontrib-programoutput [subunit] python-subunit>=1.4.3 testtools>=0.9.30 [test] zope.testing ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294461.0 zope_testrunner-7.3/src/zope.testrunner.egg-info/top_level.txt0000644000076600000240000000000515011314375024527 0ustar00m.howitzstaffzope ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1747294460.0 zope_testrunner-7.3/tox.ini0000644000076600000240000000325715011314374015655 0ustar00m.howitzstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python [tox] minversion = 3.18 envlist = release-check lint py39 py310 py311 py312 py313 pypy3 docs coverage py{39,310,311,312,313,py3}-subunit [testenv] usedevelop = true package = wheel wheel_build_env = .pkg deps = setuptools <= 75.6.0 commands = zope-testrunner --test-path=src {posargs:-vc} extras = test subunit: subunit coverage: subunit [testenv:setuptools-latest] basepython = python3 deps = git+https://github.com/pypa/setuptools.git\#egg=setuptools [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 skip_install = true deps = setuptools <= 75.6.0 twine build check-manifest check-python-versions >= 0.20.0 wheel commands_pre = commands = check-manifest check-python-versions --only setup.py,tox.ini,.github/workflows/tests.yml python -m build --sdist --no-isolation twine check dist/* [testenv:lint] description = This env runs all linters configured in .pre-commit-config.yaml basepython = python3 skip_install = true deps = pre-commit commands_pre = commands = pre-commit run --all-files --show-diff-on-failure [testenv:docs] basepython = python3 skip_install = false extras = docs commands_pre = commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html [testenv:coverage] basepython = python3 allowlist_externals = mkdir deps = coverage[toml] commands = mkdir -p {toxinidir}/parts/htmlcov coverage run -m zope.testrunner --test-path=src {posargs:-vc} coverage html coverage report