Make sure marks in subclasses don't change marks in superclasses
Fix #842
This commit is contained in:
		
							parent
							
								
									94cdec2cfe
								
							
						
					
					
						commit
						aa25fb05a9
					
				|  | @ -1,6 +1,10 @@ | ||||||
| 2.7.3 (compared to 2.7.2) | 2.7.3 (compared to 2.7.2) | ||||||
| ----------------------------- | ----------------------------- | ||||||
| 
 | 
 | ||||||
|  | - fix issue842: applying markers in classes no longer propagate this markers | ||||||
|  |   to superclasses which also have markers. | ||||||
|  |   Thanks xmo-odoo for the report and Bruno Oliveira for the PR. | ||||||
|  | 
 | ||||||
| - preserve warning functions after call to pytest.deprecated_call. Thanks | - preserve warning functions after call to pytest.deprecated_call. Thanks | ||||||
|   Pieter Mulder for PR. |   Pieter Mulder for PR. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| """ generic mechanism for marking and selecting python functions. """ | """ generic mechanism for marking and selecting python functions. """ | ||||||
|  | import inspect | ||||||
| import py | import py | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -253,15 +254,17 @@ class MarkDecorator: | ||||||
|             otherwise add *args/**kwargs in-place to mark information. """ |             otherwise add *args/**kwargs in-place to mark information. """ | ||||||
|         if args and not kwargs: |         if args and not kwargs: | ||||||
|             func = args[0] |             func = args[0] | ||||||
|             if len(args) == 1 and (istestfunc(func) or |             is_class = inspect.isclass(func) | ||||||
|                                    hasattr(func, '__bases__')): |             if len(args) == 1 and (istestfunc(func) or is_class): | ||||||
|                 if hasattr(func, '__bases__'): |                 if is_class: | ||||||
|                     if hasattr(func, 'pytestmark'): |                     if hasattr(func, 'pytestmark'): | ||||||
|                         l = func.pytestmark |                         mark_list = func.pytestmark | ||||||
|                         if not isinstance(l, list): |                         if not isinstance(mark_list, list): | ||||||
|                             func.pytestmark = [l, self] |                             mark_list = [mark_list] | ||||||
|                         else: |                         # always work on a copy to avoid updating pytestmark | ||||||
|                             l.append(self) |                         # from a superclass by accident | ||||||
|  |                         mark_list = mark_list + [self] | ||||||
|  |                         func.pytestmark = mark_list | ||||||
|                     else: |                     else: | ||||||
|                         func.pytestmark = [self] |                         func.pytestmark = [self] | ||||||
|                 else: |                 else: | ||||||
|  |  | ||||||
|  | @ -369,6 +369,45 @@ class TestFunctional: | ||||||
|             print (item, item.keywords) |             print (item, item.keywords) | ||||||
|             assert 'a' in item.keywords |             assert 'a' in item.keywords | ||||||
| 
 | 
 | ||||||
|  |     def test_mark_decorator_subclass_does_not_propagate_to_base(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             import pytest | ||||||
|  | 
 | ||||||
|  |             @pytest.mark.a | ||||||
|  |             class Base: pass | ||||||
|  | 
 | ||||||
|  |             @pytest.mark.b | ||||||
|  |             class Test1(Base): | ||||||
|  |                 def test_foo(self): pass | ||||||
|  | 
 | ||||||
|  |             class Test2(Base): | ||||||
|  |                 def test_bar(self): pass | ||||||
|  |         """) | ||||||
|  |         items, rec = testdir.inline_genitems(p) | ||||||
|  |         self.assert_markers(items, test_foo=('a', 'b'), test_bar=('a',)) | ||||||
|  | 
 | ||||||
|  |     def test_mark_decorator_baseclasses_merged(self, testdir): | ||||||
|  |         p = testdir.makepyfile(""" | ||||||
|  |             import pytest | ||||||
|  | 
 | ||||||
|  |             @pytest.mark.a | ||||||
|  |             class Base: pass | ||||||
|  | 
 | ||||||
|  |             @pytest.mark.b | ||||||
|  |             class Base2(Base): pass | ||||||
|  | 
 | ||||||
|  |             @pytest.mark.c | ||||||
|  |             class Test1(Base2): | ||||||
|  |                 def test_foo(self): pass | ||||||
|  | 
 | ||||||
|  |             class Test2(Base2): | ||||||
|  |                 @pytest.mark.d | ||||||
|  |                 def test_bar(self): pass | ||||||
|  |         """) | ||||||
|  |         items, rec = testdir.inline_genitems(p) | ||||||
|  |         self.assert_markers(items, test_foo=('a', 'b', 'c'), | ||||||
|  |                             test_bar=('a', 'b', 'd')) | ||||||
|  | 
 | ||||||
|     def test_mark_with_wrong_marker(self, testdir): |     def test_mark_with_wrong_marker(self, testdir): | ||||||
|         reprec = testdir.inline_runsource(""" |         reprec = testdir.inline_runsource(""" | ||||||
|                 import pytest |                 import pytest | ||||||
|  | @ -477,6 +516,22 @@ class TestFunctional: | ||||||
|         reprec = testdir.inline_run("-m", "mark1") |         reprec = testdir.inline_run("-m", "mark1") | ||||||
|         reprec.assertoutcome(passed=1) |         reprec.assertoutcome(passed=1) | ||||||
| 
 | 
 | ||||||
|  |     def assert_markers(self, items, **expected): | ||||||
|  |         """assert that given items have expected marker names applied to them. | ||||||
|  |         expected should be a dict of (item name -> seq of expected marker names) | ||||||
|  | 
 | ||||||
|  |         .. note:: this could be moved to ``testdir`` if proven to be useful | ||||||
|  |         to other modules. | ||||||
|  |         """ | ||||||
|  |         from _pytest.mark import MarkInfo | ||||||
|  |         items = dict((x.name, x) for x in items) | ||||||
|  |         for name, expected_markers in expected.items(): | ||||||
|  |             markers = items[name].keywords._markers | ||||||
|  |             marker_names = set([name for (name, v) in markers.items() | ||||||
|  |                                 if isinstance(v, MarkInfo)]) | ||||||
|  |             assert marker_names == set(expected_markers) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class TestKeywordSelection: | class TestKeywordSelection: | ||||||
|     def test_select_simple(self, testdir): |     def test_select_simple(self, testdir): | ||||||
|         file_test = testdir.makepyfile(""" |         file_test = testdir.makepyfile(""" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue