3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/6fa216bce897/ Changeset: 6fa216bce897 Branch: issue504 User: rouge8 Date: 2014-04-14 23:42:02 Summary: issue504: verbose output displays node IDs for each test
Replace the verbose per-test reporting format of `file:line test_name RESULT` with the node ID of the test, i.e. `file@line::class::method[param] RESULT`. This patch does not update the examples in the docs; @hpk42 has a script to regenerate those. Affected #: 8 files diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -41,3 +41,4 @@ Marc Schlaich Christopher Gilling Daniel Grana +Andy Freeland diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ NEXT (2.6) ----------------------------------- +- change -v output to include full node IDs of tests. Users can copy + a node ID from a test run, including line number, and use it as a + positional argument in order to run only a single test. + - change XPASS colour to yellow rather then red when tests are run with -v. diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -2,6 +2,7 @@ import py # DON't import pytest here because it causes import cycle troubles +import re import sys, os from _pytest import hookspec # the extension point definitions from _pytest.core import PluginManager @@ -180,7 +181,7 @@ a = option.attrs() arggroup.add_argument(*n, **a) # bash like autocompletion for dirs (appending '/') - optparser.add_argument(FILE_OR_DIR, nargs='*' + optparser.add_argument(FILE_OR_DIR, nargs='*', type=node_with_line_number, ).completer=filescompleter return optparser @@ -699,7 +700,7 @@ except ConftestImportFailure: e = sys.exc_info()[1] if ns.help or ns.version: - # we don't want to prevent --help/--version to work + # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end self.pluginmanager._warnings.append( "could not load initial conftests (%s)\n" % e.path) @@ -839,6 +840,12 @@ return {} +def node_with_line_number(string): + split = string.split('[') + split[0] = re.sub(r'@\d+', '', split[0]) + return '['.join(split) + + def setns(obj, dic): import pytest for name, value in dic.items(): diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -382,9 +382,11 @@ line = str(fspath) if lineno is not None: lineno += 1 - line += ":" + str(lineno) + line += "@" + str(lineno) if domain: - line += ": " + str(domain) + split = str(domain).split('[') + split[0] = split[0].replace('.', '::') # don't replace '.' in params + line += "::" + '['.join(split) else: line = "[location]" return line + " " diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1620,22 +1620,22 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - test_mod1.py:1: test_func[s1] PASSED - test_mod2.py:1: test_func2[s1] PASSED - test_mod2.py:3: test_func3[s1-m1] PASSED - test_mod2.py:5: test_func3b[s1-m1] PASSED - test_mod2.py:3: test_func3[s1-m2] PASSED - test_mod2.py:5: test_func3b[s1-m2] PASSED - test_mod1.py:1: test_func[s2] PASSED - test_mod2.py:1: test_func2[s2] PASSED - test_mod2.py:3: test_func3[s2-m1] PASSED - test_mod2.py:5: test_func3b[s2-m1] PASSED - test_mod2.py:7: test_func4[m1] PASSED - test_mod2.py:3: test_func3[s2-m2] PASSED - test_mod2.py:5: test_func3b[s2-m2] PASSED - test_mod2.py:7: test_func4[m2] PASSED - test_mod1.py:3: test_func1[m1] PASSED - test_mod1.py:3: test_func1[m2] PASSED + test_mod1.py@1::test_func[s1] PASSED + test_mod2.py@1::test_func2[s1] PASSED + test_mod2.py@3::test_func3[s1-m1] PASSED + test_mod2.py@5::test_func3b[s1-m1] PASSED + test_mod2.py@3::test_func3[s1-m2] PASSED + test_mod2.py@5::test_func3b[s1-m2] PASSED + test_mod1.py@1::test_func[s2] PASSED + test_mod2.py@1::test_func2[s2] PASSED + test_mod2.py@3::test_func3[s2-m1] PASSED + test_mod2.py@5::test_func3b[s2-m1] PASSED + test_mod2.py@7::test_func4[m1] PASSED + test_mod2.py@3::test_func3[s2-m2] PASSED + test_mod2.py@5::test_func3b[s2-m2] PASSED + test_mod2.py@7::test_func4[m2] PASSED + test_mod1.py@3::test_func1[m1] PASSED + test_mod1.py@3::test_func1[m2] PASSED """) def test_class_ordering(self, testdir): @@ -1672,18 +1672,18 @@ """) result = testdir.runpytest("-vs") result.stdout.fnmatch_lines(""" - test_class_ordering.py:4: TestClass2.test_1[1-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[1-b] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-b] PASSED - test_class_ordering.py:9: TestClass.test_3[1-a] PASSED - test_class_ordering.py:9: TestClass.test_3[2-a] PASSED - test_class_ordering.py:9: TestClass.test_3[1-b] PASSED - test_class_ordering.py:9: TestClass.test_3[2-b] PASSED + test_class_ordering.py@4::TestClass2::test_1[1-a] PASSED + test_class_ordering.py@4::TestClass2::test_1[2-a] PASSED + test_class_ordering.py@6::TestClass2::test_2[1-a] PASSED + test_class_ordering.py@6::TestClass2::test_2[2-a] PASSED + test_class_ordering.py@4::TestClass2::test_1[1-b] PASSED + test_class_ordering.py@4::TestClass2::test_1[2-b] PASSED + test_class_ordering.py@6::TestClass2::test_2[1-b] PASSED + test_class_ordering.py@6::TestClass2::test_2[2-b] PASSED + test_class_ordering.py@9::TestClass::test_3[1-a] PASSED + test_class_ordering.py@9::TestClass::test_3[2-a] PASSED + test_class_ordering.py@9::TestClass::test_3[1-b] PASSED + test_class_ordering.py@9::TestClass::test_3[2-b] PASSED """) def test_parametrize_separated_order_higher_scope_first(self, testdir): @@ -2094,7 +2094,7 @@ def fix1(request): def f(): raise KeyError - request.addfinalizer(f) + request.addfinalizer(f) return object() l = [] @@ -2113,7 +2113,7 @@ *KeyError* *3 pass*2 error* """) - + def test_setupfunc_missing_funcarg(self, testdir): diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/test_parseopt.py --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -145,6 +145,10 @@ assert args.R == True assert args.S == False + def test_parse_removes_line_number_from_positional_arguments(self, parser): + args = parser.parse(['path@2::func', 'path2@5::func2[param with @]']) + assert getattr(args, parseopt.FILE_OR_DIR) == ['path::func', 'path2::func2[param with @]'] + def test_parse_defaultgetter(self): def defaultget(option): if not hasattr(option, 'type'): diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -51,9 +51,9 @@ result = testdir.runpytest(*option.args) if option.verbose: result.stdout.fnmatch_lines([ - "*test_pass_skip_fail.py:2: *test_ok*PASS*", - "*test_pass_skip_fail.py:4: *test_skip*SKIP*", - "*test_pass_skip_fail.py:6: *test_func*FAIL*", + "*test_pass_skip_fail.py@2::test_ok PASS*", + "*test_pass_skip_fail.py@4::test_skip SKIP*", + "*test_pass_skip_fail.py@6::test_func FAIL*", ]) else: result.stdout.fnmatch_lines([ @@ -126,7 +126,7 @@ ]) result = testdir.runpytest("-v", p2) result.stdout.fnmatch_lines([ - "*test_p2.py <- *test_p1.py:2: TestMore.test_p1*", + "*test_p2.py <- *test_p1.py@2::TestMore::test_p1*", ]) def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir): @@ -450,17 +450,17 @@ """) result = testdir.runpytest(p1, '-v') result.stdout.fnmatch_lines([ - "*test_verbose_reporting.py:2: test_fail*FAIL*", - "*test_verbose_reporting.py:4: test_pass*PASS*", - "*test_verbose_reporting.py:7: TestClass.test_skip*SKIP*", - "*test_verbose_reporting.py:10: test_gen*FAIL*", + "*test_verbose_reporting.py@2::test_fail *FAIL*", + "*test_verbose_reporting.py@4::test_pass *PASS*", + "*test_verbose_reporting.py@7::TestClass::test_skip *SKIP*", + "*test_verbose_reporting.py@10::test_gen*0* *FAIL*", ]) assert result.ret == 1 pytestconfig.pluginmanager.skipifmissing("xdist") result = testdir.runpytest(p1, '-v', '-n 1') result.stdout.fnmatch_lines([ - "*FAIL*test_verbose_reporting.py:2: test_fail*", + "*FAIL*test_verbose_reporting.py@2::test_fail*", ]) assert result.ret == 1 diff -r a00590d97593d00163772511aca3149f7ef0bae2 -r 6fa216bce897473c4abc86e18aaf502e65d39130 testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -29,8 +29,8 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - *MyTestCaseWithRunTest.runTest* - *MyTestCaseWithoutRunTest.test_something* + *MyTestCaseWithRunTest::runTest* + *MyTestCaseWithoutRunTest::test_something* *2 passed* """) https://bitbucket.org/hpk42/pytest/commits/229f3213f7a6/ Changeset: 229f3213f7a6 User: flub Date: 2014-04-15 17:43:38 Summary: Merge pull request #158, fixes issue 504 Affected #: 8 files diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -41,3 +41,4 @@ Marc Schlaich Christopher Gilling Daniel Grana +Andy Freeland diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ NEXT (2.6) ----------------------------------- +- change -v output to include full node IDs of tests. Users can copy + a node ID from a test run, including line number, and use it as a + positional argument in order to run only a single test. + - fix issue 475: fail early and comprehensible if calling pytest.raises with wrong exception type. diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -2,6 +2,7 @@ import py # DON't import pytest here because it causes import cycle troubles +import re import sys, os from _pytest import hookspec # the extension point definitions from _pytest.core import PluginManager @@ -180,7 +181,7 @@ a = option.attrs() arggroup.add_argument(*n, **a) # bash like autocompletion for dirs (appending '/') - optparser.add_argument(FILE_OR_DIR, nargs='*' + optparser.add_argument(FILE_OR_DIR, nargs='*', type=node_with_line_number, ).completer=filescompleter return optparser @@ -699,7 +700,7 @@ except ConftestImportFailure: e = sys.exc_info()[1] if ns.help or ns.version: - # we don't want to prevent --help/--version to work + # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end self.pluginmanager._warnings.append( "could not load initial conftests (%s)\n" % e.path) @@ -839,6 +840,12 @@ return {} +def node_with_line_number(string): + split = string.split('[') + split[0] = re.sub(r'@\d+', '', split[0]) + return '['.join(split) + + def setns(obj, dic): import pytest for name, value in dic.items(): diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -382,9 +382,11 @@ line = str(fspath) if lineno is not None: lineno += 1 - line += ":" + str(lineno) + line += "@" + str(lineno) if domain: - line += ": " + str(domain) + split = str(domain).split('[') + split[0] = split[0].replace('.', '::') # don't replace '.' in params + line += "::" + '['.join(split) else: line = "[location]" return line + " " diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1620,22 +1620,22 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - test_mod1.py:1: test_func[s1] PASSED - test_mod2.py:1: test_func2[s1] PASSED - test_mod2.py:3: test_func3[s1-m1] PASSED - test_mod2.py:5: test_func3b[s1-m1] PASSED - test_mod2.py:3: test_func3[s1-m2] PASSED - test_mod2.py:5: test_func3b[s1-m2] PASSED - test_mod1.py:1: test_func[s2] PASSED - test_mod2.py:1: test_func2[s2] PASSED - test_mod2.py:3: test_func3[s2-m1] PASSED - test_mod2.py:5: test_func3b[s2-m1] PASSED - test_mod2.py:7: test_func4[m1] PASSED - test_mod2.py:3: test_func3[s2-m2] PASSED - test_mod2.py:5: test_func3b[s2-m2] PASSED - test_mod2.py:7: test_func4[m2] PASSED - test_mod1.py:3: test_func1[m1] PASSED - test_mod1.py:3: test_func1[m2] PASSED + test_mod1.py@1::test_func[s1] PASSED + test_mod2.py@1::test_func2[s1] PASSED + test_mod2.py@3::test_func3[s1-m1] PASSED + test_mod2.py@5::test_func3b[s1-m1] PASSED + test_mod2.py@3::test_func3[s1-m2] PASSED + test_mod2.py@5::test_func3b[s1-m2] PASSED + test_mod1.py@1::test_func[s2] PASSED + test_mod2.py@1::test_func2[s2] PASSED + test_mod2.py@3::test_func3[s2-m1] PASSED + test_mod2.py@5::test_func3b[s2-m1] PASSED + test_mod2.py@7::test_func4[m1] PASSED + test_mod2.py@3::test_func3[s2-m2] PASSED + test_mod2.py@5::test_func3b[s2-m2] PASSED + test_mod2.py@7::test_func4[m2] PASSED + test_mod1.py@3::test_func1[m1] PASSED + test_mod1.py@3::test_func1[m2] PASSED """) def test_class_ordering(self, testdir): @@ -1672,18 +1672,18 @@ """) result = testdir.runpytest("-vs") result.stdout.fnmatch_lines(""" - test_class_ordering.py:4: TestClass2.test_1[1-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-a] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-a] PASSED - test_class_ordering.py:4: TestClass2.test_1[1-b] PASSED - test_class_ordering.py:4: TestClass2.test_1[2-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[1-b] PASSED - test_class_ordering.py:6: TestClass2.test_2[2-b] PASSED - test_class_ordering.py:9: TestClass.test_3[1-a] PASSED - test_class_ordering.py:9: TestClass.test_3[2-a] PASSED - test_class_ordering.py:9: TestClass.test_3[1-b] PASSED - test_class_ordering.py:9: TestClass.test_3[2-b] PASSED + test_class_ordering.py@4::TestClass2::test_1[1-a] PASSED + test_class_ordering.py@4::TestClass2::test_1[2-a] PASSED + test_class_ordering.py@6::TestClass2::test_2[1-a] PASSED + test_class_ordering.py@6::TestClass2::test_2[2-a] PASSED + test_class_ordering.py@4::TestClass2::test_1[1-b] PASSED + test_class_ordering.py@4::TestClass2::test_1[2-b] PASSED + test_class_ordering.py@6::TestClass2::test_2[1-b] PASSED + test_class_ordering.py@6::TestClass2::test_2[2-b] PASSED + test_class_ordering.py@9::TestClass::test_3[1-a] PASSED + test_class_ordering.py@9::TestClass::test_3[2-a] PASSED + test_class_ordering.py@9::TestClass::test_3[1-b] PASSED + test_class_ordering.py@9::TestClass::test_3[2-b] PASSED """) def test_parametrize_separated_order_higher_scope_first(self, testdir): @@ -2094,7 +2094,7 @@ def fix1(request): def f(): raise KeyError - request.addfinalizer(f) + request.addfinalizer(f) return object() l = [] @@ -2113,7 +2113,7 @@ *KeyError* *3 pass*2 error* """) - + def test_setupfunc_missing_funcarg(self, testdir): diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/test_parseopt.py --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -145,6 +145,10 @@ assert args.R == True assert args.S == False + def test_parse_removes_line_number_from_positional_arguments(self, parser): + args = parser.parse(['path@2::func', 'path2@5::func2[param with @]']) + assert getattr(args, parseopt.FILE_OR_DIR) == ['path::func', 'path2::func2[param with @]'] + def test_parse_defaultgetter(self): def defaultget(option): if not hasattr(option, 'type'): diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -51,9 +51,9 @@ result = testdir.runpytest(*option.args) if option.verbose: result.stdout.fnmatch_lines([ - "*test_pass_skip_fail.py:2: *test_ok*PASS*", - "*test_pass_skip_fail.py:4: *test_skip*SKIP*", - "*test_pass_skip_fail.py:6: *test_func*FAIL*", + "*test_pass_skip_fail.py@2::test_ok PASS*", + "*test_pass_skip_fail.py@4::test_skip SKIP*", + "*test_pass_skip_fail.py@6::test_func FAIL*", ]) else: result.stdout.fnmatch_lines([ @@ -126,7 +126,7 @@ ]) result = testdir.runpytest("-v", p2) result.stdout.fnmatch_lines([ - "*test_p2.py <- *test_p1.py:2: TestMore.test_p1*", + "*test_p2.py <- *test_p1.py@2::TestMore::test_p1*", ]) def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir): @@ -450,17 +450,17 @@ """) result = testdir.runpytest(p1, '-v') result.stdout.fnmatch_lines([ - "*test_verbose_reporting.py:2: test_fail*FAIL*", - "*test_verbose_reporting.py:4: test_pass*PASS*", - "*test_verbose_reporting.py:7: TestClass.test_skip*SKIP*", - "*test_verbose_reporting.py:10: test_gen*FAIL*", + "*test_verbose_reporting.py@2::test_fail *FAIL*", + "*test_verbose_reporting.py@4::test_pass *PASS*", + "*test_verbose_reporting.py@7::TestClass::test_skip *SKIP*", + "*test_verbose_reporting.py@10::test_gen*0* *FAIL*", ]) assert result.ret == 1 pytestconfig.pluginmanager.skipifmissing("xdist") result = testdir.runpytest(p1, '-v', '-n 1') result.stdout.fnmatch_lines([ - "*FAIL*test_verbose_reporting.py:2: test_fail*", + "*FAIL*test_verbose_reporting.py@2::test_fail*", ]) assert result.ret == 1 diff -r 9572842277fa4cf0e9cac75684b4fb02c3927f4d -r 229f3213f7a629f547e6b89a20263a41611807a1 testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -29,8 +29,8 @@ """) result = testdir.runpytest("-v") result.stdout.fnmatch_lines(""" - *MyTestCaseWithRunTest.runTest* - *MyTestCaseWithoutRunTest.test_something* + *MyTestCaseWithRunTest::runTest* + *MyTestCaseWithoutRunTest::test_something* *2 passed* """) https://bitbucket.org/hpk42/pytest/commits/ffe92a9b0441/ Changeset: ffe92a9b0441 Branch: issue504 User: flub Date: 2014-04-15 17:46:34 Summary: Closing bugfix branch Affected #: 0 files Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. _______________________________________________ pytest-commit mailing list pytest-commit@python.org https://mail.python.org/mailman/listinfo/pytest-commit