Skip to content

Commit e4daa4d

Browse files
committed
Implementation of markers
- 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/
1 parent 9c2ec45 commit e4daa4d

9 files changed

+399
-39
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ Sergei Chipiga <[email protected]>
33
Ben Greene <[email protected]>
44
Adam Talsma <[email protected]>
55
Brian Maissy <[email protected]>
6+
Jonas Zinn <[email protected]>

example/test.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def test_foo():
2+
assert True
3+
4+
5+
def test_bar():
6+
assert True

example/test_number.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pytest
2+
3+
4+
@pytest.mark.run(order=-2)
5+
def test_three():
6+
assert True
7+
8+
9+
@pytest.mark.run(order=-1)
10+
def test_four():
11+
assert True
12+
13+
14+
@pytest.mark.run(order=2)
15+
def test_two():
16+
assert True
17+
18+
19+
@pytest.mark.run(order=1)
20+
def test_one():
21+
assert True

example/test_ordinals.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pytest
2+
3+
4+
@pytest.mark.run('second_to_last')
5+
def test_three():
6+
assert True
7+
8+
9+
@pytest.mark.run('last')
10+
def test_four():
11+
assert True
12+
13+
14+
@pytest.mark.run('second')
15+
def test_two():
16+
assert True
17+
18+
19+
@pytest.mark.run('first')
20+
def test_one():
21+
assert True

example/test_other.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import pytest
2+
3+
4+
@pytest.mark.second_to_last
5+
def test_three():
6+
assert True
7+
8+
9+
@pytest.mark.last
10+
def test_four():
11+
assert True
12+
13+
14+
@pytest.mark.second
15+
def test_two():
16+
assert True
17+
18+
19+
@pytest.mark.first
20+
def test_one():
21+
assert True

example/test_quickstart.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import pytest
2+
3+
4+
@pytest.mark.order2
5+
def test_foo():
6+
assert True
7+
8+
9+
@pytest.mark.order1
10+
def test_bar():
11+
assert True

example/test_relative.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import pytest
2+
3+
4+
@pytest.mark.run(after='test_second')
5+
def test_third():
6+
assert True
7+
8+
9+
def test_second():
10+
assert True
11+
12+
13+
@pytest.mark.run(before='test_second')
14+
def test_first():
15+
assert True

pytest_ordering/__init__.py

+147-34
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# -*- coding: utf-8 -*-
2-
from ._version import __version__
3-
4-
import operator
2+
import os
3+
import re
4+
import sys
55

6-
import pytest
6+
from ._version import __version__
77

88
orders_map = {
99
'first': 0,
@@ -21,7 +21,7 @@
2121
'fifth_to_last': -5,
2222
'sixth_to_last': -6,
2323
'seventh_to_last': -7,
24-
'eighth_to_last': -8,
24+
'eighth_to_last': -8
2525
}
2626

2727

@@ -34,8 +34,8 @@ def pytest_configure(config):
3434
)
3535

3636
config_line = (
37-
'run: specify ordering information for when tests should run '
38-
'in relation to one another. ' + provided_by_pytest_ordering
37+
'run: specify ordering information for when tests should run '
38+
'in relation to one another. ' + provided_by_pytest_ordering
3939
)
4040
config.addinivalue_line('markers', config_line)
4141

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

4848

49-
def pytest_collection_modifyitems(session, config, items):
50-
grouped_items = {}
51-
52-
for item in items:
53-
54-
for mark_name, order in orders_map.items():
55-
mark = item.get_closest_marker(mark_name)
49+
def get_filename(item):
50+
name = item.location[0]
51+
if os.sep in name:
52+
name = item.location[0].rsplit(os.sep, 1)[1]
53+
return name[:-3]
5654

57-
if mark:
58-
item.add_marker(pytest.mark.run(order=order))
59-
break
6055

56+
def mark_binning(item, keys, start, end, before, after, unordered):
57+
match_order = re.compile(r"order(\d+)(:?,|$)")
58+
find_order = match_order.search(",".join(keys))
59+
if find_order:
60+
order = int(find_order.group(1))
61+
start.setdefault(order, []).append(item)
62+
return True
63+
elif "run" in keys:
6164
mark = item.get_closest_marker('run')
62-
63-
if mark:
64-
order = mark.kwargs.get('order')
65+
order = mark.kwargs.get('order')
66+
before_mark = mark.kwargs.get('before')
67+
after_mark = mark.kwargs.get('after')
68+
if order is not None:
69+
order = int(order)
70+
if order < 0:
71+
end.setdefault(order, []).append(item)
72+
else:
73+
start.setdefault(order, []).append(item)
74+
elif before_mark:
75+
if "." not in before_mark:
76+
prefix = get_filename(item)
77+
before_mark = prefix + "." + before_mark
78+
before.setdefault(before_mark, []).append(item)
79+
elif after_mark:
80+
if "." not in after_mark:
81+
prefix = get_filename(item)
82+
after_mark = prefix + "." + after_mark
83+
84+
after.setdefault(after_mark, []).append(item)
6585
else:
66-
order = None
67-
68-
grouped_items.setdefault(order, []).append(item)
69-
70-
sorted_items = []
71-
unordered_items = [grouped_items.pop(None, [])]
86+
for ordinal, position in orders_map.items():
87+
if ordinal in mark.args:
88+
if position < 0:
89+
end.setdefault(position, []).append(item)
90+
else:
91+
start.setdefault(position, []).append(item)
92+
break
93+
return True
94+
for mark_name, order in orders_map.items():
95+
mark = item.get_closest_marker(mark_name)
96+
if mark:
97+
order = int(order)
98+
if order < 0:
99+
end.setdefault(order, []).append(item)
100+
else:
101+
start.setdefault(order, []).append(item)
102+
return True
103+
unordered.append(item)
104+
return False
105+
106+
107+
def insert(items, sort):
108+
list_items = []
109+
if isinstance(items, tuple):
110+
list_items = items[1]
111+
else:
112+
list_items = items
113+
sort += list_items
114+
115+
116+
def insert_before(name, items, sort):
117+
regex_name = re.escape(name) + r"(:?\.\w+)?$"
118+
for pos, item in enumerate(sort):
119+
prefix = get_filename(item)
120+
item_name = prefix + "." + item.location[2]
121+
if re.match(regex_name, item_name):
122+
if pos == 0:
123+
sort[:] = items + sort
124+
else:
125+
sort[pos:1] = items
126+
return True
127+
return False
128+
129+
130+
def insert_after(name, items, sort):
131+
regex_name = re.escape(name) + r"(:?\.\w+)?$"
132+
for pos, item in reversed(list(enumerate(sort))):
133+
prefix = get_filename(item)
134+
item_name = prefix + "." + item.location[2]
135+
if re.match(regex_name, item_name):
136+
sort[pos + 1:1] = items
137+
return True
138+
139+
return False
72140

73-
start_list = sorted((i for i in grouped_items.items() if i[0] >= 0),
74-
key=operator.itemgetter(0))
75-
end_list = sorted((i for i in grouped_items.items() if i[0] < 0),
76-
key=operator.itemgetter(0))
77141

78-
sorted_items.extend([i[1] for i in start_list])
79-
sorted_items.extend(unordered_items)
80-
sorted_items.extend([i[1] for i in end_list])
142+
def pytest_collection_modifyitems(session, config, items):
143+
before_item = {}
144+
after_item = {}
145+
start_item = {}
146+
end_item = {}
147+
unordered_list = []
81148

82-
items[:] = [item for sublist in sorted_items for item in sublist]
149+
for item in items:
150+
mark_binning(item, item.keywords.keys(), start_item, end_item,
151+
before_item, after_item, unordered_list)
152+
153+
start_item = sorted(start_item.items())
154+
end_item = sorted(end_item.items())
155+
156+
sorted_list = []
157+
158+
for entries in start_item:
159+
insert(entries, sorted_list)
160+
insert(unordered_list, sorted_list)
161+
for entries in end_item:
162+
insert(entries, sorted_list)
163+
164+
still_left = 0
165+
length = len(before_item) + len(after_item)
166+
167+
while still_left != length:
168+
still_left = length
169+
remove_labels = []
170+
for label, before in before_item.items():
171+
if insert_before(label, before, sorted_list):
172+
remove_labels.append(label)
173+
for label in remove_labels:
174+
del before_item[label]
175+
176+
remove_labels = []
177+
for label, after in after_item.items():
178+
if insert_after(label, after, sorted_list):
179+
remove_labels.append(label)
180+
for label in remove_labels:
181+
del after_item[label]
182+
183+
length = len(before_item) + len(after_item)
184+
if length:
185+
sys.stdout.write("WARNING: can not execute test relative to others: ")
186+
for label, entry in before_item.items():
187+
sys.stdout.write(label + " ")
188+
sorted_list += entry
189+
for label, entry in after_item.items():
190+
sys.stdout.write(label + " ")
191+
sorted_list += entry
192+
sys.stdout.flush()
193+
print("enqueue them behind the others")
194+
195+
items[:] = sorted_list

0 commit comments

Comments
 (0)