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) | ||||
| ----------------------------- | ||||
| 
 | ||||
| - 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 | ||||
|   Pieter Mulder for PR. | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| """ generic mechanism for marking and selecting python functions. """ | ||||
| import inspect | ||||
| import py | ||||
| 
 | ||||
| 
 | ||||
|  | @ -253,15 +254,17 @@ class MarkDecorator: | |||
|             otherwise add *args/**kwargs in-place to mark information. """ | ||||
|         if args and not kwargs: | ||||
|             func = args[0] | ||||
|             if len(args) == 1 and (istestfunc(func) or | ||||
|                                    hasattr(func, '__bases__')): | ||||
|                 if hasattr(func, '__bases__'): | ||||
|             is_class = inspect.isclass(func) | ||||
|             if len(args) == 1 and (istestfunc(func) or is_class): | ||||
|                 if is_class: | ||||
|                     if hasattr(func, 'pytestmark'): | ||||
|                         l = func.pytestmark | ||||
|                         if not isinstance(l, list): | ||||
|                             func.pytestmark = [l, self] | ||||
|                         else: | ||||
|                             l.append(self) | ||||
|                         mark_list = func.pytestmark | ||||
|                         if not isinstance(mark_list, list): | ||||
|                             mark_list = [mark_list] | ||||
|                         # always work on a copy to avoid updating pytestmark | ||||
|                         # from a superclass by accident | ||||
|                         mark_list = mark_list + [self] | ||||
|                         func.pytestmark = mark_list | ||||
|                     else: | ||||
|                         func.pytestmark = [self] | ||||
|                 else: | ||||
|  |  | |||
|  | @ -369,6 +369,45 @@ class TestFunctional: | |||
|             print (item, 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): | ||||
|         reprec = testdir.inline_runsource(""" | ||||
|                 import pytest | ||||
|  | @ -477,6 +516,22 @@ class TestFunctional: | |||
|         reprec = testdir.inline_run("-m", "mark1") | ||||
|         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: | ||||
|     def test_select_simple(self, testdir): | ||||
|         file_test = testdir.makepyfile(""" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue