From 8770ea4bda7a078ef967dddf1791d6a5bc1401cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Wed, 22 Jul 2020 12:13:43 +0200 Subject: [PATCH 1/5] Add relative to other tests 'run' mark 'after' and 'before' kwargs support. --- pytest_ordering/__init__.py | 34 ++++++++++++++++++++++++++++++++++ tests/test_ordering.py | 15 +++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/pytest_ordering/__init__.py b/pytest_ordering/__init__.py index c8eb64a..9edb224 100644 --- a/pytest_ordering/__init__.py +++ b/pytest_ordering/__init__.py @@ -48,6 +48,7 @@ def pytest_configure(config): def pytest_collection_modifyitems(session, config, items): grouped_items = {} + before_items, after_items = ({}, {}) for item in items: @@ -62,6 +63,20 @@ def pytest_collection_modifyitems(session, config, items): if mark: order = mark.kwargs.get('order') + if order is None: + before = mark.kwargs.get('before') + if before: + if not before in before_items: + before_items[before] = [] + before_items[before].append(item) + continue + + after = mark.kwargs.get('after') + if after: + if not after in after_items: + after_items[after] = [] + after_items[after].append(item) + continue else: order = None @@ -80,3 +95,22 @@ def pytest_collection_modifyitems(session, config, items): sorted_items.extend([i[1] for i in end_list]) items[:] = [item for sublist in sorted_items for item in sublist] + + def _get_item_index_by_name(item_name): + index = None + for i, item in enumerate(items): + if getattr(item, "name") == item_name: + index = i + break + return index + + for before_item_relative, _before_items in before_items.items(): + index = _get_item_index_by_name(before_item_relative) + if index is not None: + for before_item in _before_items: + items.insert(index, before_item) + for after_item_relative, _after_items in after_items.items(): + index = _get_item_index_by_name(after_item_relative) + if index is not None: + for after_item in _after_items: + items.insert(index+1, after_item) diff --git a/tests/test_ordering.py b/tests/test_ordering.py index dd703ab..fb4bb41 100644 --- a/tests/test_ordering.py +++ b/tests/test_ordering.py @@ -268,6 +268,21 @@ def test_5(self): pass assert item_names_for(tests_content) == ['test_3', 'test_4', 'test_5', 'test_1', 'test_2'] +def test_relative_to_other_tests(item_names_for): + tests_content = """ + import pytest + + @pytest.mark.run(after='test_2') + def test_3(): pass + + def test_2(): pass + + @pytest.mark.run(before='test_2') + def test_1(): pass + """ + assert item_names_for(tests_content) == ['test_1', 'test_2', 'test_3'] + + def test_markers_registered(capsys): pytest.main(['--markers']) out, err = capsys.readouterr() From 59800ad3b16789fc44314eb02cb3096bce7c9e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Thu, 23 Jul 2020 13:55:50 +0200 Subject: [PATCH 2/5] Update with requested changes. --- docs/source/index.rst | 82 ++++++++++++++++++------------------- pytest_ordering/__init__.py | 52 +++++++++++++++++++---- tests/test_ordering.py | 50 ++++++++++++++++++++++ 3 files changed, 134 insertions(+), 50 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 35cd398..ffe1d26 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -121,6 +121,47 @@ You can also use markers such as "first", "second", "last", and "second_to_last" =========================== 4 passed in 0.02 seconds =========================== +Relative to other tests +----------------------- + +Tests can be defined relative to others with "before" and "after" parameters. + +.. code:: python + + import pytest + + @pytest.mark.run(after='test_second') + def test_third(): + assert True + + def test_second(): + assert True + + @pytest.mark.run(before='test_second') + def test_first(): + assert True + +:: + + $ py.test test_foo.py -vv + ============================= test session starts ============================== + platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python + plugins: ordering + collected 3 items + + test_foo.py:11: test_first PASSED + test_foo.py:7: test_second PASSED + test_foo.py:4: test_third PASSED + + =========================== 4 passed in 0.02 seconds =========================== + + + + .. toctree:: + :maxdepth: 2 + + .. _markers: https://pytest.org/latest/mark.html + Aspirational ============ @@ -204,44 +245,3 @@ By number =========================== 4 passed in 0.02 seconds =========================== - -Relative to other tests ------------------------ - - -.. code:: python - - import pytest - - @pytest.mark.run(after='test_second') - def test_third(): - assert True - - def test_second(): - assert True - - @pytest.mark.run(before='test_second') - def test_first(): - assert True - -:: - - $ py.test test_foo.py -vv - ============================= test session starts ============================== - platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python - plugins: ordering - collected 3 items - - test_foo.py:11: test_first PASSED - test_foo.py:7: test_second PASSED - test_foo.py:4: test_third PASSED - - =========================== 4 passed in 0.02 seconds =========================== - - - -.. toctree:: - :maxdepth: 2 - -.. _markers: https://pytest.org/latest/mark.html - diff --git a/pytest_ordering/__init__.py b/pytest_ordering/__init__.py index 9edb224..d565e04 100644 --- a/pytest_ordering/__init__.py +++ b/pytest_ordering/__init__.py @@ -2,6 +2,7 @@ from ._version import __version__ import operator +import warnings import pytest @@ -47,8 +48,7 @@ def pytest_configure(config): def pytest_collection_modifyitems(session, config, items): - grouped_items = {} - before_items, after_items = ({}, {}) + grouped_items, before_items, after_items = ({}, {}, {}) for item in items: @@ -66,16 +66,12 @@ def pytest_collection_modifyitems(session, config, items): if order is None: before = mark.kwargs.get('before') if before: - if not before in before_items: - before_items[before] = [] - before_items[before].append(item) + before_items.setdefault(before, []).append(item) continue after = mark.kwargs.get('after') if after: - if not after in after_items: - after_items[after] = [] - after_items[after].append(item) + after_items.setdefault(after, []).append(item) continue else: order = None @@ -99,7 +95,7 @@ def pytest_collection_modifyitems(session, config, items): def _get_item_index_by_name(item_name): index = None for i, item in enumerate(items): - if getattr(item, "name") == item_name: + if getattr(item, 'name') == item_name: index = i break return index @@ -109,8 +105,46 @@ def _get_item_index_by_name(item_name): if index is not None: for before_item in _before_items: items.insert(index, before_item) + else: + if len(_before_items) == 1: + message_schema = "%s test, indicated at parameter before of" \ + + " %s test, doesn't exists" + message = message_schema % (before_item_relative, + _before_items[0].name) + else: + message_schema = "%s test, indicated at parameter before of" \ + + " %s tests, doesn't exists" + test_names = "" + for i, before_item in enumerate(_before_items): + test_names += before_item.name + if i < len(_before_items) - 2: + test_names += ", " + elif i == len(_before_items) - 2: + test_names += " and " + message = message_schema % (before_item_relative, test_names) + warnings.warn(message, SyntaxWarning) + items.extend(_before_items) for after_item_relative, _after_items in after_items.items(): index = _get_item_index_by_name(after_item_relative) if index is not None: for after_item in _after_items: items.insert(index+1, after_item) + else: + if len(_after_items) == 1: + message_schema = "%s test, indicated at parameter after of" \ + + " %s test, doesn't exists" + message = message_schema % (after_item_relative, + _after_items[0].name) + else: + message_schema = "%s test, indicated at parameter after of" \ + + " %s tests, doesn't exists" + test_names = "" + for i, after_item in enumerate(_after_items): + test_names += after_item.name + if i < len(_after_items) - 2: + test_names += ", " + elif i == len(_after_items) - 2: + test_names += " and " + message = message_schema % (after_item_relative, test_names) + warnings.warn(message, SyntaxWarning) + items.extend(_after_items) diff --git a/tests/test_ordering.py b/tests/test_ordering.py index fb4bb41..afbb02e 100644 --- a/tests/test_ordering.py +++ b/tests/test_ordering.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import re +import warnings import pytest @@ -282,6 +283,55 @@ def test_1(): pass """ assert item_names_for(tests_content) == ['test_1', 'test_2', 'test_3'] +def test_relative_to_other_invalid_tests(item_names_for): + tests_content = """ + import pytest + + @pytest.mark.run(before='test_A') + def test_1(): pass + + def test_2(): pass + + @pytest.mark.run(after='test_B') + def test_3(): pass + + @pytest.mark.run(before='test_C') + def test_4(): pass + + @pytest.mark.run(before='test_C') + def test_5(): pass + + @pytest.mark.run(before='test_C') + def test_6(): pass + + @pytest.mark.run(after='test_D') + def test_7(): pass + + @pytest.mark.run(after='test_D') + def test_8(): pass + """ + + with warnings.catch_warnings(record=True) as catched_warnings: + assert item_names_for(tests_content) == [ + 'test_2', 'test_1', 'test_4', 'test_5', + 'test_6', 'test_3', 'test_7', 'test_8', + ] + + expected_warning_messages = [ + "test_A test, indicated at parameter before of test_1 test, doesn't exists", + "test_B test, indicated at parameter after of test_3 test, doesn't exists", + "test_C test, indicated at parameter before of test_4, test_5 and test_6 tests, doesn't exists", + "test_D test, indicated at parameter after of test_7 and test_8 tests, doesn't exists", + ] + + n_other_warnings = 0 + for w in catched_warnings: + if not issubclass(w.category, SyntaxWarning): + n_other_warnings += 1 + continue + assert w.message.__str__() in expected_warning_messages + assert len(expected_warning_messages) + n_other_warnings == len(catched_warnings) + def test_markers_registered(capsys): pytest.main(['--markers']) From 59fb5a92b294ce18cd60299f84332ca91707cf99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Thu, 23 Jul 2020 14:00:16 +0200 Subject: [PATCH 3/5] Simplify warning messages. --- pytest_ordering/__init__.py | 16 ++++++++-------- tests/test_ordering.py | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pytest_ordering/__init__.py b/pytest_ordering/__init__.py index d565e04..1a04be9 100644 --- a/pytest_ordering/__init__.py +++ b/pytest_ordering/__init__.py @@ -107,13 +107,13 @@ def _get_item_index_by_name(item_name): items.insert(index, before_item) else: if len(_before_items) == 1: - message_schema = "%s test, indicated at parameter before of" \ - + " %s test, doesn't exists" + message_schema = "%s, indicated at parameter before of" \ + + " %s, doesn't exists" message = message_schema % (before_item_relative, _before_items[0].name) else: - message_schema = "%s test, indicated at parameter before of" \ - + " %s tests, doesn't exists" + message_schema = "%s, indicated at parameter before of" \ + + " %s, doesn't exists" test_names = "" for i, before_item in enumerate(_before_items): test_names += before_item.name @@ -131,13 +131,13 @@ def _get_item_index_by_name(item_name): items.insert(index+1, after_item) else: if len(_after_items) == 1: - message_schema = "%s test, indicated at parameter after of" \ - + " %s test, doesn't exists" + message_schema = "%s, indicated at parameter after of" \ + + " %s, doesn't exists" message = message_schema % (after_item_relative, _after_items[0].name) else: - message_schema = "%s test, indicated at parameter after of" \ - + " %s tests, doesn't exists" + message_schema = "%s, indicated at parameter after of" \ + + " %s, doesn't exists" test_names = "" for i, after_item in enumerate(_after_items): test_names += after_item.name diff --git a/tests/test_ordering.py b/tests/test_ordering.py index afbb02e..c635ab7 100644 --- a/tests/test_ordering.py +++ b/tests/test_ordering.py @@ -318,10 +318,10 @@ def test_8(): pass ] expected_warning_messages = [ - "test_A test, indicated at parameter before of test_1 test, doesn't exists", - "test_B test, indicated at parameter after of test_3 test, doesn't exists", - "test_C test, indicated at parameter before of test_4, test_5 and test_6 tests, doesn't exists", - "test_D test, indicated at parameter after of test_7 and test_8 tests, doesn't exists", + "test_A, indicated at parameter before of test_1, doesn't exists", + "test_B, indicated at parameter after of test_3, doesn't exists", + "test_C, indicated at parameter before of test_4, test_5 and test_6, doesn't exists", + "test_D, indicated at parameter after of test_7 and test_8, doesn't exists", ] n_other_warnings = 0 From c3ab10fa60b8a5a5dd627e978bcb623ecb0bd63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Thu, 23 Jul 2020 19:18:06 +0200 Subject: [PATCH 4/5] Fix typo --- pytest_ordering/__init__.py | 8 ++++---- tests/test_ordering.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pytest_ordering/__init__.py b/pytest_ordering/__init__.py index 1a04be9..2a20b00 100644 --- a/pytest_ordering/__init__.py +++ b/pytest_ordering/__init__.py @@ -108,12 +108,12 @@ def _get_item_index_by_name(item_name): else: if len(_before_items) == 1: message_schema = "%s, indicated at parameter before of" \ - + " %s, doesn't exists" + + " %s, doesn't exist" message = message_schema % (before_item_relative, _before_items[0].name) else: message_schema = "%s, indicated at parameter before of" \ - + " %s, doesn't exists" + + " %s, doesn't exist" test_names = "" for i, before_item in enumerate(_before_items): test_names += before_item.name @@ -132,12 +132,12 @@ def _get_item_index_by_name(item_name): else: if len(_after_items) == 1: message_schema = "%s, indicated at parameter after of" \ - + " %s, doesn't exists" + + " %s, doesn't exist" message = message_schema % (after_item_relative, _after_items[0].name) else: message_schema = "%s, indicated at parameter after of" \ - + " %s, doesn't exists" + + " %s, doesn't exist" test_names = "" for i, after_item in enumerate(_after_items): test_names += after_item.name diff --git a/tests/test_ordering.py b/tests/test_ordering.py index c635ab7..91e93c2 100644 --- a/tests/test_ordering.py +++ b/tests/test_ordering.py @@ -318,10 +318,10 @@ def test_8(): pass ] expected_warning_messages = [ - "test_A, indicated at parameter before of test_1, doesn't exists", - "test_B, indicated at parameter after of test_3, doesn't exists", - "test_C, indicated at parameter before of test_4, test_5 and test_6, doesn't exists", - "test_D, indicated at parameter after of test_7 and test_8, doesn't exists", + "test_A, indicated at parameter before of test_1, doesn't exist", + "test_B, indicated at parameter after of test_3, doesn't exist", + "test_C, indicated at parameter before of test_4, test_5 and test_6, doesn't exist", + "test_D, indicated at parameter after of test_7 and test_8, doesn't exist", ] n_other_warnings = 0 From 6c38beb7b41a4d5b8e0b97534904514b15b1a1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar?= Date: Thu, 23 Jul 2020 20:14:19 +0200 Subject: [PATCH 5/5] Fix error in tests for Python2 --- tests/test_ordering.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_ordering.py b/tests/test_ordering.py index 91e93c2..828c768 100644 --- a/tests/test_ordering.py +++ b/tests/test_ordering.py @@ -312,10 +312,12 @@ def test_8(): pass """ with warnings.catch_warnings(record=True) as catched_warnings: - assert item_names_for(tests_content) == [ + expected_item_names = [ 'test_2', 'test_1', 'test_4', 'test_5', 'test_6', 'test_3', 'test_7', 'test_8', ] + for item_name in item_names_for(tests_content): + assert item_name in expected_item_names expected_warning_messages = [ "test_A, indicated at parameter before of test_1, doesn't exist",