Imported Upstream version python3-pytest-repeat 0.8.0 upstream upstream/0.8.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 12 Apr 2021 06:19:14 +0000 (15:19 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 12 Apr 2021 06:19:14 +0000 (15:19 +0900)
16 files changed:
.travis.yml [new file with mode: 0644]
CHANGES.rst [new file with mode: 0644]
LICENSE [new file with mode: 0644]
PKG-INFO [new file with mode: 0644]
README.rst [new file with mode: 0644]
pytest_repeat.egg-info/PKG-INFO [new file with mode: 0644]
pytest_repeat.egg-info/SOURCES.txt [new file with mode: 0644]
pytest_repeat.egg-info/dependency_links.txt [new file with mode: 0644]
pytest_repeat.egg-info/entry_points.txt [new file with mode: 0644]
pytest_repeat.egg-info/requires.txt [new file with mode: 0644]
pytest_repeat.egg-info/top_level.txt [new file with mode: 0644]
pytest_repeat.py [new file with mode: 0644]
setup.cfg [new file with mode: 0644]
setup.py [new file with mode: 0644]
test_repeat.py [new file with mode: 0644]
tox.ini [new file with mode: 0644]

diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..274da80
--- /dev/null
@@ -0,0 +1,34 @@
+language: python
+python:
+- 2.7
+- 3.4
+- 3.5
+- 3.6
+- pypy
+- pypy3
+- nightly
+env:
+- TOXENV=pytest36
+- TOXENV=pytest37
+- TOXENV=pytest38
+matrix:
+  include:
+  - python: 2.7
+    env: TOXENV=flake8
+  - python: 3.5
+    env: TOXENV=flake8
+install:
+- pip install tox-travis
+script:
+- tox
+deploy:
+  provider: pypi
+  user: davehunt
+  password:
+    secure: IuoBL03YKh7rZw78aQqRrlD6YcvwSPK6nKTpoxFc3uiljOufyfV3s4rGffSxEl4gAkMlBz3SXnfPKJPqNizfp3TEJXrc84TBO/Iq+U7j43rWyUluzJ0okPgU4892aalQiEvVyrrjEaMWA5ndhX7tkyTICN0BfHlKPmMrN+1mq73y4eUQl49CJWsbhF8Qnc5tcDh2athtgFA3fG0kSWDZHdw02i2JC1qlm+WvqBfw813BeUnmR55HKRgPQMJ2whUzab7m2QBg0HkjniNXaqS+aYY8fMGrntNtEDRrWO130ioeUqpFQvg7FoigbE5eQBX07Z50lpJn5CuCgnMzOrDpkXMy7Y+k+MyxKFUWaaqZjZiKolaPSs2qW5zggmCD4ECa5g8BpfpBkUj+D6iFkYyZIJUdq2jX0JFA536L9H2E1KOATxE8u0utQvkKcJhHgWVa0iw0q14MWSVLcZ8OdzP0Bo5nVVICcZAyIj7FQn0pIRnoXQ7cIpGbmMeJUBT9n08hyUYWoxfXKNt8KNkKDww81nAVphuz6ic1KwEKIDt3MJlHHbazjt59/+SBU+L0bDhS5ZHZkHAVHyl3EP18DCAYZWJjR4TZxlLRb2o3BUTcSwqFcIlTGw1O1DbZ/LTaGRRUxwh41AY98DKxrujxerALgX3MIlaS6WkM1FbOdO+ZB1Q=
+  distributions: sdist bdist_wheel
+  on:
+    tags: true
+    repo: pytest-dev/pytest-repeat
+    python: 3.5
+    condition: "$TOXENV = pytest38"
diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644 (file)
index 0000000..0e3d759
--- /dev/null
@@ -0,0 +1,49 @@
+Release Notes
+-------------
+
+**0.8.0 (2019-02-26)**
+
+* Fix mark deprecation warnings in new pytest versions.
+
+* ``pytest-repeat`` now requires pytest>=3.6.
+
+**0.7.0 (2018-08-23)**
+
+* Move step number to the end of the parametrisation ID
+
+  * Thanks to `@gdyuldin <https://github.com/gdyuldin>`_ for suggesting
+    this enhancement and providing a patch
+
+**0.6.0 (2018-08-01)**
+
+* Add option for controlling the scope of the repeat parameterisation
+
+  * Thanks to `@gdyuldin <https://github.com/gdyuldin>`_ for suggesting
+    this enhancement and providing a patch
+
+**0.5.0 (2018-07-19)**
+
+* Allow repeating a test using a decorator  (`#16 <https://github.com/pytest-dev/pytest-repeat/issues/16>`_)
+
+  * Thanks to `@Peque <https://github.com/Peque>`_ for suggesting
+    this enhancement and providing a patch
+
+**0.4.0 (2016-08-25)**
+
+* No changes - testing deploy to PyPI from Travis CI
+
+**0.4.0 (2016-08-09)**
+
+* Fix deprecation warning present in pytest 3.0 for type argument in addoption
+
+**0.3.0 (2016-06-30)**
+
+* Added support for repeating parameterised tests
+
+**0.2 (2015-10-27)**
+
+* README updates
+
+**0.1 (2015-10-19)**
+
+* Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..1e55f5d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,3 @@
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at https://www.mozilla.org/en-US/MPL/2.0/.
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644 (file)
index 0000000..2143105
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,123 @@
+Metadata-Version: 1.1
+Name: pytest-repeat
+Version: 0.8.0
+Summary: pytest plugin for repeating tests
+Home-page: https://github.com/pytest-dev/pytest-repeat
+Author: Bob Silverberg
+Author-email: bsilverberg@mozilla.com
+License: Mozilla Public License 2.0 (MPL 2.0)
+Description: pytest-repeat
+        ===================
+        
+        pytest-repeat is a plugin for `py.test <https://docs.pytest.org>`_ that makes it
+        easy to repeat a single test, or multiple tests, a specific number of times.
+        
+        .. image:: https://img.shields.io/badge/license-MPL%202.0-blue.svg
+           :target: https://github.com/pytest-dev/pytest-repeat/blob/master/LICENSE
+           :alt: License
+        .. image:: https://img.shields.io/pypi/v/pytest-repeat.svg
+           :target: https://pypi.python.org/pypi/pytest-repeat/
+           :alt: PyPI
+        .. image:: https://img.shields.io/travis/pytest-dev/pytest-repeat.svg
+           :target: https://travis-ci.org/pytest-dev/pytest-repeat/
+           :alt: Travis
+        .. image:: https://img.shields.io/github/issues-raw/pytest-dev/pytest-repeat.svg
+           :target: https://github.com/pytest-dev/pytest-repeat/issues
+           :alt: Issues
+        .. image:: https://img.shields.io/requires/github/pytest-dev/pytest-repeat.svg
+           :target: https://requires.io/github/pytest-dev/pytest-repeat/requirements/?branch=master
+           :alt: Requirements
+        
+        Requirements
+        ------------
+        
+        You will need the following prerequisites in order to use pytest-repeat:
+        
+        - Python 2.7, 3.4+ or PyPy
+        - py.test 2.8 or newer
+        
+        Installation
+        ------------
+        To install pytest-repeat:
+        
+        .. code-block:: bash
+        
+          $ pip install pytest-repeat
+        
+        Repeating a test
+        ----------------
+        
+        Use the :code:`--count` command line option to specify how many times you want
+        your test, or tests, to be run:
+        
+        .. code-block:: bash
+        
+          $ py.test --count=10 test_file.py
+        
+        Each test collected by py.test will be run :code:`count` times.
+        
+        If you want to mark a test in your code to be repeated a number of times, you
+        can use the :code:`@pytest.mark.repeat(count)` decorator:
+        
+        .. code-block:: python
+        
+           import pytest
+        
+        
+           @pytest.mark.repeat(3)
+           def test_repeat_decorator():
+               pass
+        
+        If you want to override default tests executions order, you can use :code:`--repeat-scope`
+        command line option with one of the next values: :code:`session`,  :code:`module`, :code:`class` or :code:`function` (default).
+        It behaves like a scope of the pytest fixture.
+        
+        :code:`function` (default) scope repeats each test :code:`count` or :code:`repeat` times before executing next test.
+        :code:`session` scope repeats whole tests session, i.e. all collected tests executed once, then all such tests executed again and etc.
+        :code:`class` and :code:`module` behaves similar :code:`session` , but repeating set of tests is a tests from class or module, not all collected tests.
+        
+        Repeating a test until failure
+        ------------------------------
+        
+        If you are trying to diagnose an intermittent failure, it can be useful to run the same
+        test over and over again until it fails. You can use pytest's :code:`-x` option in
+        conjunction with pytest-repeat to force the test runner to stop at the first failure.
+        For example:
+        
+        .. code-block:: bash
+        
+          $ py.test --count=1000 -x test_file.py
+        
+        This will attempt to run test_file.py 1000 times, but will stop as soon as a failure
+        occurs.
+        
+        UnitTest Style Tests
+        --------------------
+        
+        Unfortunately pytest-repeat is not able to work with unittest.TestCase test classes.
+        These tests will simply always run once, regardless of :code:`--count`, and show a warning.
+        
+        Resources
+        ---------
+        
+        - `Release Notes <https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst>`_
+        - `Issue Tracker <https://github.com/pytest-dev/pytest-repeat/issues>`_
+        - `Code <https://github.com/pytest-dev/pytest-repeat/>`_
+        
+Keywords: py.test pytest repeat
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Framework :: Pytest
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Quality Assurance
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
diff --git a/README.rst b/README.rst
new file mode 100644 (file)
index 0000000..73293eb
--- /dev/null
@@ -0,0 +1,97 @@
+pytest-repeat
+===================
+
+pytest-repeat is a plugin for `py.test <https://docs.pytest.org>`_ that makes it
+easy to repeat a single test, or multiple tests, a specific number of times.
+
+.. image:: https://img.shields.io/badge/license-MPL%202.0-blue.svg
+   :target: https://github.com/pytest-dev/pytest-repeat/blob/master/LICENSE
+   :alt: License
+.. image:: https://img.shields.io/pypi/v/pytest-repeat.svg
+   :target: https://pypi.python.org/pypi/pytest-repeat/
+   :alt: PyPI
+.. image:: https://img.shields.io/travis/pytest-dev/pytest-repeat.svg
+   :target: https://travis-ci.org/pytest-dev/pytest-repeat/
+   :alt: Travis
+.. image:: https://img.shields.io/github/issues-raw/pytest-dev/pytest-repeat.svg
+   :target: https://github.com/pytest-dev/pytest-repeat/issues
+   :alt: Issues
+.. image:: https://img.shields.io/requires/github/pytest-dev/pytest-repeat.svg
+   :target: https://requires.io/github/pytest-dev/pytest-repeat/requirements/?branch=master
+   :alt: Requirements
+
+Requirements
+------------
+
+You will need the following prerequisites in order to use pytest-repeat:
+
+- Python 2.7, 3.4+ or PyPy
+- py.test 2.8 or newer
+
+Installation
+------------
+To install pytest-repeat:
+
+.. code-block:: bash
+
+  $ pip install pytest-repeat
+
+Repeating a test
+----------------
+
+Use the :code:`--count` command line option to specify how many times you want
+your test, or tests, to be run:
+
+.. code-block:: bash
+
+  $ py.test --count=10 test_file.py
+
+Each test collected by py.test will be run :code:`count` times.
+
+If you want to mark a test in your code to be repeated a number of times, you
+can use the :code:`@pytest.mark.repeat(count)` decorator:
+
+.. code-block:: python
+
+   import pytest
+
+
+   @pytest.mark.repeat(3)
+   def test_repeat_decorator():
+       pass
+
+If you want to override default tests executions order, you can use :code:`--repeat-scope`
+command line option with one of the next values: :code:`session`,  :code:`module`, :code:`class` or :code:`function` (default).
+It behaves like a scope of the pytest fixture.
+
+:code:`function` (default) scope repeats each test :code:`count` or :code:`repeat` times before executing next test.
+:code:`session` scope repeats whole tests session, i.e. all collected tests executed once, then all such tests executed again and etc.
+:code:`class` and :code:`module` behaves similar :code:`session` , but repeating set of tests is a tests from class or module, not all collected tests.
+
+Repeating a test until failure
+------------------------------
+
+If you are trying to diagnose an intermittent failure, it can be useful to run the same
+test over and over again until it fails. You can use pytest's :code:`-x` option in
+conjunction with pytest-repeat to force the test runner to stop at the first failure.
+For example:
+
+.. code-block:: bash
+
+  $ py.test --count=1000 -x test_file.py
+
+This will attempt to run test_file.py 1000 times, but will stop as soon as a failure
+occurs.
+
+UnitTest Style Tests
+--------------------
+
+Unfortunately pytest-repeat is not able to work with unittest.TestCase test classes.
+These tests will simply always run once, regardless of :code:`--count`, and show a warning.
+
+Resources
+---------
+
+- `Release Notes <https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst>`_
+- `Issue Tracker <https://github.com/pytest-dev/pytest-repeat/issues>`_
+- `Code <https://github.com/pytest-dev/pytest-repeat/>`_
diff --git a/pytest_repeat.egg-info/PKG-INFO b/pytest_repeat.egg-info/PKG-INFO
new file mode 100644 (file)
index 0000000..2143105
--- /dev/null
@@ -0,0 +1,123 @@
+Metadata-Version: 1.1
+Name: pytest-repeat
+Version: 0.8.0
+Summary: pytest plugin for repeating tests
+Home-page: https://github.com/pytest-dev/pytest-repeat
+Author: Bob Silverberg
+Author-email: bsilverberg@mozilla.com
+License: Mozilla Public License 2.0 (MPL 2.0)
+Description: pytest-repeat
+        ===================
+        
+        pytest-repeat is a plugin for `py.test <https://docs.pytest.org>`_ that makes it
+        easy to repeat a single test, or multiple tests, a specific number of times.
+        
+        .. image:: https://img.shields.io/badge/license-MPL%202.0-blue.svg
+           :target: https://github.com/pytest-dev/pytest-repeat/blob/master/LICENSE
+           :alt: License
+        .. image:: https://img.shields.io/pypi/v/pytest-repeat.svg
+           :target: https://pypi.python.org/pypi/pytest-repeat/
+           :alt: PyPI
+        .. image:: https://img.shields.io/travis/pytest-dev/pytest-repeat.svg
+           :target: https://travis-ci.org/pytest-dev/pytest-repeat/
+           :alt: Travis
+        .. image:: https://img.shields.io/github/issues-raw/pytest-dev/pytest-repeat.svg
+           :target: https://github.com/pytest-dev/pytest-repeat/issues
+           :alt: Issues
+        .. image:: https://img.shields.io/requires/github/pytest-dev/pytest-repeat.svg
+           :target: https://requires.io/github/pytest-dev/pytest-repeat/requirements/?branch=master
+           :alt: Requirements
+        
+        Requirements
+        ------------
+        
+        You will need the following prerequisites in order to use pytest-repeat:
+        
+        - Python 2.7, 3.4+ or PyPy
+        - py.test 2.8 or newer
+        
+        Installation
+        ------------
+        To install pytest-repeat:
+        
+        .. code-block:: bash
+        
+          $ pip install pytest-repeat
+        
+        Repeating a test
+        ----------------
+        
+        Use the :code:`--count` command line option to specify how many times you want
+        your test, or tests, to be run:
+        
+        .. code-block:: bash
+        
+          $ py.test --count=10 test_file.py
+        
+        Each test collected by py.test will be run :code:`count` times.
+        
+        If you want to mark a test in your code to be repeated a number of times, you
+        can use the :code:`@pytest.mark.repeat(count)` decorator:
+        
+        .. code-block:: python
+        
+           import pytest
+        
+        
+           @pytest.mark.repeat(3)
+           def test_repeat_decorator():
+               pass
+        
+        If you want to override default tests executions order, you can use :code:`--repeat-scope`
+        command line option with one of the next values: :code:`session`,  :code:`module`, :code:`class` or :code:`function` (default).
+        It behaves like a scope of the pytest fixture.
+        
+        :code:`function` (default) scope repeats each test :code:`count` or :code:`repeat` times before executing next test.
+        :code:`session` scope repeats whole tests session, i.e. all collected tests executed once, then all such tests executed again and etc.
+        :code:`class` and :code:`module` behaves similar :code:`session` , but repeating set of tests is a tests from class or module, not all collected tests.
+        
+        Repeating a test until failure
+        ------------------------------
+        
+        If you are trying to diagnose an intermittent failure, it can be useful to run the same
+        test over and over again until it fails. You can use pytest's :code:`-x` option in
+        conjunction with pytest-repeat to force the test runner to stop at the first failure.
+        For example:
+        
+        .. code-block:: bash
+        
+          $ py.test --count=1000 -x test_file.py
+        
+        This will attempt to run test_file.py 1000 times, but will stop as soon as a failure
+        occurs.
+        
+        UnitTest Style Tests
+        --------------------
+        
+        Unfortunately pytest-repeat is not able to work with unittest.TestCase test classes.
+        These tests will simply always run once, regardless of :code:`--count`, and show a warning.
+        
+        Resources
+        ---------
+        
+        - `Release Notes <https://github.com/pytest-dev/pytest-repeat/blob/master/CHANGES.rst>`_
+        - `Issue Tracker <https://github.com/pytest-dev/pytest-repeat/issues>`_
+        - `Code <https://github.com/pytest-dev/pytest-repeat/>`_
+        
+Keywords: py.test pytest repeat
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Framework :: Pytest
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Quality Assurance
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
diff --git a/pytest_repeat.egg-info/SOURCES.txt b/pytest_repeat.egg-info/SOURCES.txt
new file mode 100644 (file)
index 0000000..277de5f
--- /dev/null
@@ -0,0 +1,16 @@
+.gitignore
+.travis.yml
+CHANGES.rst
+LICENSE
+README.rst
+pytest_repeat.py
+setup.cfg
+setup.py
+test_repeat.py
+tox.ini
+pytest_repeat.egg-info/PKG-INFO
+pytest_repeat.egg-info/SOURCES.txt
+pytest_repeat.egg-info/dependency_links.txt
+pytest_repeat.egg-info/entry_points.txt
+pytest_repeat.egg-info/requires.txt
+pytest_repeat.egg-info/top_level.txt
\ No newline at end of file
diff --git a/pytest_repeat.egg-info/dependency_links.txt b/pytest_repeat.egg-info/dependency_links.txt
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/pytest_repeat.egg-info/entry_points.txt b/pytest_repeat.egg-info/entry_points.txt
new file mode 100644 (file)
index 0000000..400d7c5
--- /dev/null
@@ -0,0 +1,3 @@
+[pytest11]
+repeat = pytest_repeat
+
diff --git a/pytest_repeat.egg-info/requires.txt b/pytest_repeat.egg-info/requires.txt
new file mode 100644 (file)
index 0000000..d2940d2
--- /dev/null
@@ -0,0 +1 @@
+pytest>=3.6
diff --git a/pytest_repeat.egg-info/top_level.txt b/pytest_repeat.egg-info/top_level.txt
new file mode 100644 (file)
index 0000000..da63fdb
--- /dev/null
@@ -0,0 +1 @@
+pytest_repeat
diff --git a/pytest_repeat.py b/pytest_repeat.py
new file mode 100644 (file)
index 0000000..411b7f7
--- /dev/null
@@ -0,0 +1,70 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://www.mozilla.org/en-US/MPL/2.0/.
+import warnings
+from unittest import TestCase
+
+import pytest
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        '--count',
+        action='store',
+        default=1,
+        type=int,
+        help='Number of times to repeat each test')
+
+    parser.addoption(
+        '--repeat-scope',
+        action='store',
+        default='function',
+        type=str,
+        choices=('function', 'class', 'module', 'session'),
+        help='Scope for repeating tests')
+
+
+def pytest_configure(config):
+    config.addinivalue_line(
+        'markers',
+        'repeat(n): run the given test function `n` times.')
+
+
+class UnexpectedError(Exception):
+    pass
+
+
+@pytest.fixture(autouse=True)
+def __pytest_repeat_step_number(request):
+    if request.config.option.count > 1:
+        try:
+            return request.param
+        except AttributeError:
+            if issubclass(request.cls, TestCase):
+                warnings.warn(
+                    "Repeating unittest class tests not supported")
+            else:
+                raise UnexpectedError(
+                    "This call couldn't work with pytest-repeat. "
+                    "Please consider raising an issue with your usage.")
+
+
+@pytest.hookimpl(trylast=True)
+def pytest_generate_tests(metafunc):
+    count = metafunc.config.option.count
+    m = metafunc.definition.get_closest_marker('repeat')
+    if m is not None:
+        count = int(m.args[0])
+    if count > 1:
+
+        def make_progress_id(i, n=count):
+            return '{0}-{1}'.format(i + 1, n)
+
+        scope = metafunc.config.option.repeat_scope
+        metafunc.parametrize(
+            '__pytest_repeat_step_number',
+            range(count),
+            indirect=True,
+            ids=make_progress_id,
+            scope=scope
+        )
diff --git a/setup.cfg b/setup.cfg
new file mode 100644 (file)
index 0000000..adf5ed7
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,7 @@
+[bdist_wheel]
+universal = 1
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..3e42eab
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,31 @@
+from setuptools import setup
+
+setup(name='pytest-repeat',
+      use_scm_version=True,
+      description='pytest plugin for repeating tests',
+      long_description=open('README.rst').read(),
+      author='Bob Silverberg',
+      author_email='bsilverberg@mozilla.com',
+      url='https://github.com/pytest-dev/pytest-repeat',
+      py_modules=['pytest_repeat'],
+      entry_points={'pytest11': ['repeat = pytest_repeat']},
+      setup_requires=['setuptools_scm'],
+      install_requires=['pytest>=3.6'],
+      license='Mozilla Public License 2.0 (MPL 2.0)',
+      keywords='py.test pytest repeat',
+      classifiers=[
+          'Development Status :: 5 - Production/Stable',
+          'Framework :: Pytest',
+          'Intended Audience :: Developers',
+          'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
+          'Operating System :: POSIX',
+          'Operating System :: Microsoft :: Windows',
+          'Operating System :: MacOS :: MacOS X',
+          'Topic :: Software Development :: Quality Assurance',
+          'Topic :: Software Development :: Testing',
+          'Topic :: Utilities',
+          'Programming Language :: Python',
+          'Programming Language :: Python :: 2.7',
+          'Programming Language :: Python :: 3.4',
+          'Programming Language :: Python :: 3.5',
+          'Programming Language :: Python :: 3.6'])
diff --git a/test_repeat.py b/test_repeat.py
new file mode 100644 (file)
index 0000000..220ccc1
--- /dev/null
@@ -0,0 +1,259 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://www.mozilla.org/en-US/MPL/2.0/.
+
+import pytest
+
+pytest_plugins = "pytester",
+
+
+class TestRepeat:
+
+    def test_no_repeat(self, testdir):
+        testdir.makepyfile("""
+            def test_no_repeat():
+                pass
+        """)
+        result = testdir.runpytest('-v', '--count', '1')
+        result.stdout.fnmatch_lines([
+            '*test_no_repeat.py::test_no_repeat PASSED*',
+            '*1 passed*',
+        ])
+        assert result.ret == 0
+
+    def test_can_repeat(self, testdir):
+        testdir.makepyfile("""
+            def test_repeat():
+                pass
+        """)
+        result = testdir.runpytest('--count', '2')
+        result.stdout.fnmatch_lines(['*2 passed*'])
+        assert result.ret == 0
+
+    def test_mark_repeat_decorator_is_registered(self, testdir):
+        result = testdir.runpytest('--markers')
+        result.stdout.fnmatch_lines([
+            '@pytest.mark.repeat(n): run the given test function `n` times.'])
+        assert result.ret == 0
+
+    def test_mark_repeat_decorator(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            @pytest.mark.repeat(3)
+            def test_mark_repeat_decorator():
+                pass
+        """)
+        result = testdir.runpytest()
+        result.stdout.fnmatch_lines(['*3 passed*'])
+        assert result.ret == 0
+
+    def test_parametrize(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            @pytest.mark.parametrize('x', ['a', 'b', 'c'])
+            def test_repeat(x):
+                pass
+        """)
+        result = testdir.runpytest('-v', '--count', '2')
+        result.stdout.fnmatch_lines([
+            '*test_parametrize.py::test_repeat[[]a-1-2[]] PASSED*',
+            '*test_parametrize.py::test_repeat[[]a-2-2[]] PASSED*',
+            '*test_parametrize.py::test_repeat[[]b-1-2[]] PASSED*',
+            '*test_parametrize.py::test_repeat[[]b-2-2[]] PASSED*',
+            '*test_parametrize.py::test_repeat[[]c-1-2[]] PASSED*',
+            '*test_parametrize.py::test_repeat[[]c-2-2[]] PASSED*',
+            '*6 passed*',
+        ])
+        assert result.ret == 0
+
+    def test_parametrized_fixture(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            @pytest.fixture(params=['a', 'b', 'c'])
+            def parametrized_fixture(request):
+                return request.param
+
+            def test_repeat(parametrized_fixture):
+                pass
+        """)
+        result = testdir.runpytest('--count', '2')
+        result.stdout.fnmatch_lines(['*6 passed*'])
+        assert result.ret == 0
+
+    def test_step_number(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            expected_steps = iter(range(5))
+            def test_repeat(__pytest_repeat_step_number):
+                assert next(expected_steps) == __pytest_repeat_step_number
+                if __pytest_repeat_step_number == 4:
+                    assert not list(expected_steps)
+        """)
+        result = testdir.runpytest('-v', '--count', '5')
+        result.stdout.fnmatch_lines([
+            '*test_step_number.py::test_repeat[[]1-5[]] PASSED*',
+            '*test_step_number.py::test_repeat[[]2-5[]] PASSED*',
+            '*test_step_number.py::test_repeat[[]3-5[]] PASSED*',
+            '*test_step_number.py::test_repeat[[]4-5[]] PASSED*',
+            '*test_step_number.py::test_repeat[[]5-5[]] PASSED*',
+            '*5 passed*',
+        ])
+        assert result.ret == 0
+
+    def test_invalid_option(self, testdir):
+        testdir.makepyfile("""
+            def test_repeat():
+                pass
+        """)
+        result = testdir.runpytest('--count', 'a')
+        assert result.ret == 4
+
+    def test_unittest_test(self, testdir):
+        testdir.makepyfile("""
+            from unittest import TestCase
+
+            class ClassStyleTest(TestCase):
+                def test_this(self):
+                    assert 1
+        """)
+        result = testdir.runpytest('-v', '--count', '2')
+        result.stdout.fnmatch_lines([
+            '*test_unittest_test.py::ClassStyleTest::test_this PASSED*',
+            '*1 passed*',
+        ])
+
+    @pytest.mark.parametrize(['scope', 'lines'], [
+        ('session', [
+            '*test_1.py::test_repeat1[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat1[[]2-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]2-2[]] PASSED*',
+            '*16 passed*',
+        ]),
+        ('module', [
+            '*test_1.py::test_repeat1[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat1[[]2-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]2-2[]] PASSED*',
+            '*16 passed*',
+        ]),
+        ('class', [
+            '*test_1.py::test_repeat1[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat1[[]2-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]2-2[]] PASSED*',
+            '*16 passed*',
+        ]),
+        ('function', [
+            '*test_1.py::test_repeat1[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat1[[]2-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]1-2[]] PASSED*',
+            '*test_1.py::test_repeat2[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat3[[]2-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]1-2[]] PASSED*',
+            '*test_2.py::test_repeat4[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat5[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat1::test_repeat6[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat7[[]2-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]1-2[]] PASSED*',
+            '*test_3.py::TestRepeat2::test_repeat8[[]2-2[]] PASSED*',
+            '*16 passed*',
+        ]),
+    ])
+    def test_scope(self, testdir, scope, lines):
+        testdir.makepyfile(test_1="""
+            def test_repeat1():
+                pass
+
+            def test_repeat2():
+                pass
+        """)
+        testdir.makepyfile(test_2="""
+            def test_repeat3():
+                pass
+
+            def test_repeat4():
+                pass
+        """)
+        testdir.makepyfile(test_3="""
+            class TestRepeat1(object):
+                def test_repeat5(self):
+                    pass
+                def test_repeat6(self):
+                    pass
+            class TestRepeat2(object):
+                def test_repeat7(self):
+                    pass
+                def test_repeat8(self):
+                    pass
+        """)
+        result = testdir.runpytest('-v', '--count', '2', '--repeat-scope',
+                                   scope)
+        result.stdout.fnmatch_lines(lines)
+        assert result.ret == 0
+
+    def test_omitted_scope(self, testdir):
+        testdir.makepyfile("""
+            def test_repeat1():
+                pass
+
+            def test_repeat2():
+                pass
+        """)
+        result = testdir.runpytest('-v', '--count', '2')
+        result.stdout.fnmatch_lines([
+            '*test_omitted_scope.py::test_repeat1[[]1-2[]] PASSED*',
+            '*test_omitted_scope.py::test_repeat1[[]2-2[]] PASSED*',
+            '*test_omitted_scope.py::test_repeat2[[]1-2[]] PASSED*',
+            '*test_omitted_scope.py::test_repeat2[[]2-2[]] PASSED*',
+            '*4 passed*',
+        ])
+        assert result.ret == 0
+
+    def test_invalid_scope(self, testdir):
+        testdir.makepyfile("""
+            def test_repeat():
+                pass
+        """)
+        result = testdir.runpytest('--count', '2', '--repeat-scope', 'a')
+        assert result.ret == 4
diff --git a/tox.ini b/tox.ini
new file mode 100644 (file)
index 0000000..9c59b9a
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,19 @@
+# Tox (https://tox.readthedocs.io/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py{27,34,35,36,py,py3}-pytest{36,37,38}, flake8
+
+[testenv]
+commands = pytest {posargs}
+deps =
+    pytest36: pytest~=3.6
+    pytest37: pytest~=3.7
+    pytest38: pytest~=3.8
+
+[testenv:flake8]
+basepython = python
+deps = flake8
+commands = flake8 {posargs:pytest_repeat.py test_repeat.py}