Skip to content

Commit

Permalink
Implementation of markers
Browse files Browse the repository at this point in the history
- manually merged from PR ftobia#37 by @zinnjonas
- supports multiple markers like run(before=), run(after=) etc.
- see http://pytest-ordering.readthedocs.io/en/develop/
  • Loading branch information
mrbean-bremen committed Oct 20, 2020
1 parent 063573e commit e25516c
Show file tree
Hide file tree
Showing 9 changed files with 399 additions and 39 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Sergei Chipiga <[email protected]>
Ben Greene <[email protected]>
Adam Talsma <[email protected]>
Brian Maissy <[email protected]>
Jonas Zinn <[email protected]>
6 changes: 6 additions & 0 deletions example/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def test_foo():
assert True


def test_bar():
assert True
21 changes: 21 additions & 0 deletions example/test_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pytest


@pytest.mark.run(order=-2)
def test_three():
assert True


@pytest.mark.run(order=-1)
def test_four():
assert True


@pytest.mark.run(order=2)
def test_two():
assert True


@pytest.mark.run(order=1)
def test_one():
assert True
21 changes: 21 additions & 0 deletions example/test_ordinals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pytest


@pytest.mark.run('second_to_last')
def test_three():
assert True


@pytest.mark.run('last')
def test_four():
assert True


@pytest.mark.run('second')
def test_two():
assert True


@pytest.mark.run('first')
def test_one():
assert True
21 changes: 21 additions & 0 deletions example/test_other.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pytest


@pytest.mark.second_to_last
def test_three():
assert True


@pytest.mark.last
def test_four():
assert True


@pytest.mark.second
def test_two():
assert True


@pytest.mark.first
def test_one():
assert True
11 changes: 11 additions & 0 deletions example/test_quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import pytest


@pytest.mark.order2
def test_foo():
assert True


@pytest.mark.order1
def test_bar():
assert True
15 changes: 15 additions & 0 deletions example/test_relative.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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
181 changes: 147 additions & 34 deletions pytest_ordering/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from ._version import __version__

import operator
import os
import re
import sys

import pytest
from ._version import __version__

orders_map = {
'first': 0,
Expand All @@ -21,7 +21,7 @@
'fifth_to_last': -5,
'sixth_to_last': -6,
'seventh_to_last': -7,
'eighth_to_last': -8,
'eighth_to_last': -8
}


Expand All @@ -34,8 +34,8 @@ def pytest_configure(config):
)

config_line = (
'run: specify ordering information for when tests should run '
'in relation to one another. ' + provided_by_pytest_ordering
'run: specify ordering information for when tests should run '
'in relation to one another. ' + provided_by_pytest_ordering
)
config.addinivalue_line('markers', config_line)

Expand All @@ -46,37 +46,150 @@ def pytest_configure(config):
config.addinivalue_line('markers', config_line)


def pytest_collection_modifyitems(session, config, items):
grouped_items = {}

for item in items:

for mark_name, order in orders_map.items():
mark = item.get_closest_marker(mark_name)
def get_filename(item):
name = item.location[0]
if os.sep in name:
name = item.location[0].rsplit(os.sep, 1)[1]
return name[:-3]

if mark:
item.add_marker(pytest.mark.run(order=order))
break

def mark_binning(item, keys, start, end, before, after, unordered):
match_order = re.compile(r"order(\d+)(:?,|$)")
find_order = match_order.search(",".join(keys))
if find_order:
order = int(find_order.group(1))
start.setdefault(order, []).append(item)
return True
elif "run" in keys:
mark = item.get_closest_marker('run')

if mark:
order = mark.kwargs.get('order')
order = mark.kwargs.get('order')
before_mark = mark.kwargs.get('before')
after_mark = mark.kwargs.get('after')
if order is not None:
order = int(order)
if order < 0:
end.setdefault(order, []).append(item)
else:
start.setdefault(order, []).append(item)
elif before_mark:
if "." not in before_mark:
prefix = get_filename(item)
before_mark = prefix + "." + before_mark
before.setdefault(before_mark, []).append(item)
elif after_mark:
if "." not in after_mark:
prefix = get_filename(item)
after_mark = prefix + "." + after_mark

after.setdefault(after_mark, []).append(item)
else:
order = None

grouped_items.setdefault(order, []).append(item)

sorted_items = []
unordered_items = [grouped_items.pop(None, [])]
for ordinal, position in orders_map.items():
if ordinal in mark.args:
if position < 0:
end.setdefault(position, []).append(item)
else:
start.setdefault(position, []).append(item)
break
return True
for mark_name, order in orders_map.items():
mark = item.get_closest_marker(mark_name)
if mark:
order = int(order)
if order < 0:
end.setdefault(order, []).append(item)
else:
start.setdefault(order, []).append(item)
return True
unordered.append(item)
return False


def insert(items, sort):
list_items = []
if isinstance(items, tuple):
list_items = items[1]
else:
list_items = items
sort += list_items


def insert_before(name, items, sort):
regex_name = re.escape(name) + r"(:?\.\w+)?$"
for pos, item in enumerate(sort):
prefix = get_filename(item)
item_name = prefix + "." + item.location[2]
if re.match(regex_name, item_name):
if pos == 0:
sort[:] = items + sort
else:
sort[pos:1] = items
return True
return False


def insert_after(name, items, sort):
regex_name = re.escape(name) + r"(:?\.\w+)?$"
for pos, item in reversed(list(enumerate(sort))):
prefix = get_filename(item)
item_name = prefix + "." + item.location[2]
if re.match(regex_name, item_name):
sort[pos + 1:1] = items
return True

return False

start_list = sorted((i for i in grouped_items.items() if i[0] >= 0),
key=operator.itemgetter(0))
end_list = sorted((i for i in grouped_items.items() if i[0] < 0),
key=operator.itemgetter(0))

sorted_items.extend([i[1] for i in start_list])
sorted_items.extend(unordered_items)
sorted_items.extend([i[1] for i in end_list])
def pytest_collection_modifyitems(session, config, items):
before_item = {}
after_item = {}
start_item = {}
end_item = {}
unordered_list = []

items[:] = [item for sublist in sorted_items for item in sublist]
for item in items:
mark_binning(item, item.keywords.keys(), start_item, end_item,
before_item, after_item, unordered_list)

start_item = sorted(start_item.items())
end_item = sorted(end_item.items())

sorted_list = []

for entries in start_item:
insert(entries, sorted_list)
insert(unordered_list, sorted_list)
for entries in end_item:
insert(entries, sorted_list)

still_left = 0
length = len(before_item) + len(after_item)

while still_left != length:
still_left = length
remove_labels = []
for label, before in before_item.items():
if insert_before(label, before, sorted_list):
remove_labels.append(label)
for label in remove_labels:
del before_item[label]

remove_labels = []
for label, after in after_item.items():
if insert_after(label, after, sorted_list):
remove_labels.append(label)
for label in remove_labels:
del after_item[label]

length = len(before_item) + len(after_item)
if length:
sys.stdout.write("WARNING: can not execute test relative to others: ")
for label, entry in before_item.items():
sys.stdout.write(label + " ")
sorted_list += entry
for label, entry in after_item.items():
sys.stdout.write(label + " ")
sorted_list += entry
sys.stdout.flush()
print("enqueue them behind the others")

items[:] = sorted_list
Loading

0 comments on commit e25516c

Please sign in to comment.