From d3e363b97a2a27fecc3c049bcd1fae9fa4ac34f5 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Tue, 28 Apr 2015 11:54:45 +0200 Subject: [PATCH] - make API between runpytest() and inline_run() more similar - shift a number of tests to become inline_run() tests --HG-- branch : testrefactor --- _pytest/config.py | 24 ++++---- _pytest/main.py | 5 +- _pytest/pytester.py | 35 ++++++++++-- testing/python/collect.py | 36 ++++++------ testing/python/fixture.py | 114 ++++++++++++++++++------------------- testing/python/metafunc.py | 60 +++++++++---------- testing/test_assertion.py | 2 +- testing/test_config.py | 10 ++-- testing/test_nose.py | 36 ++++++------ 9 files changed, 169 insertions(+), 153 deletions(-) diff --git a/_pytest/config.py b/_pytest/config.py index f37d417b2..fcad07146 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -29,17 +29,21 @@ def main(args=None, plugins=None): initialization. """ try: - config = _prepareconfig(args, plugins) - except ConftestImportFailure: - e = sys.exc_info()[1] - tw = py.io.TerminalWriter(sys.stderr) - for line in traceback.format_exception(*e.excinfo): - tw.line(line.rstrip(), red=True) - tw.line("ERROR: could not load %s\n" % (e.path), red=True) + try: + config = _prepareconfig(args, plugins) + except ConftestImportFailure as e: + tw = py.io.TerminalWriter(sys.stderr) + for line in traceback.format_exception(*e.excinfo): + tw.line(line.rstrip(), red=True) + tw.line("ERROR: could not load %s\n" % (e.path), red=True) + return 4 + else: + config.pluginmanager.check_pending() + return config.hook.pytest_cmdline_main(config=config) + except UsageError as e: + for msg in e.args: + sys.stderr.write("ERROR: %s\n" %(msg,)) return 4 - else: - config.pluginmanager.check_pending() - return config.hook.pytest_cmdline_main(config=config) class cmdline: # compatibility namespace main = staticmethod(main) diff --git a/_pytest/main.py b/_pytest/main.py index b13da3529..737b7f20a 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -83,10 +83,7 @@ def wrap_session(config, doit): initstate = 2 doit(config, session) except pytest.UsageError: - args = sys.exc_info()[1].args - for msg in args: - sys.stderr.write("ERROR: %s\n" %(msg,)) - session.exitstatus = EXIT_USAGEERROR + raise except KeyboardInterrupt: excinfo = py.code.ExceptionInfo() config.hook.pytest_keyboard_interrupt(excinfo=excinfo) diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 09517e8ff..b99fb709e 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -204,6 +204,8 @@ def pytest_funcarg__testdir(request): tmptestdir = TmpTestdir(request) return tmptestdir + + rex_outcome = re.compile("(\d+) (\w+)") class RunResult: """The result of running a command. @@ -229,6 +231,8 @@ class RunResult: self.duration = duration def parseoutcomes(self): + """ Return a dictionary of outcomestring->num from parsing + the terminal output that the test process produced.""" for line in reversed(self.outlines): if 'seconds' in line: outcomes = rex_outcome.findall(line) @@ -238,13 +242,16 @@ class RunResult: d[cat] = int(num) return d - def assertoutcome(self, passed=0, skipped=0, failed=0): + def assert_outcomes(self, passed=0, skipped=0, failed=0): + """ assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" d = self.parseoutcomes() assert passed == d.get("passed", 0) assert skipped == d.get("skipped", 0) assert failed == d.get("failed", 0) + class TmpTestdir: """Temporary test directory with tools to test/run py.test itself. @@ -568,12 +575,32 @@ class TmpTestdir: plugins = kwargs.get("plugins") or [] plugins.append(Collect()) ret = pytest.main(list(args), plugins=plugins) - assert len(rec) == 1 - reprec = rec[0] - reprec.ret = ret self.delete_loaded_modules() + if len(rec) == 1: + reprec = rec.pop() + else: + class reprec: + pass + reprec.ret = ret return reprec + def inline_runpytest(self, *args): + """ Return result of running pytest in-process, providing a similar + interface to what self.runpytest() provides. """ + now = time.time() + capture = py.io.StdCaptureFD() + try: + reprec = self.inline_run(*args) + finally: + out, err = capture.reset() + assert out or err + + res = RunResult(reprec.ret, + out.split("\n"), err.split("\n"), + time.time()-now) + res.reprec = reprec + return res + def parseconfig(self, *args): """Return a new py.test Config instance from given commandline args. diff --git a/testing/python/collect.py b/testing/python/collect.py index 6629d0833..998ee8bc9 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -15,7 +15,7 @@ class TestModule: p.pyimport() del py.std.sys.modules['test_whatever'] b.ensure("test_whatever.py") - result = testdir.runpytest() + result = testdir.inline_runpytest() result.stdout.fnmatch_lines([ "*import*mismatch*", "*imported*test_whatever*", @@ -59,7 +59,7 @@ class TestClass: def __init__(self): pass """) - result = testdir.runpytest("-rw") + result = testdir.inline_runpytest("-rw") result.stdout.fnmatch_lines_random(""" WC1*test_class_with_init_warning.py*__init__* """) @@ -69,7 +69,7 @@ class TestClass: class test(object): pass """) - result = testdir.runpytest() + result = testdir.inline_runpytest() result.stdout.fnmatch_lines([ "*collected 0*", ]) @@ -86,7 +86,7 @@ class TestClass: def teardown_class(cls): pass """) - result = testdir.runpytest() + result = testdir.inline_runpytest() result.stdout.fnmatch_lines([ "*1 passed*", ]) @@ -534,7 +534,7 @@ class TestConftestCustomization: """) testdir.makepyfile("def test_some(): pass") testdir.makepyfile(test_xyz="def test_func(): pass") - result = testdir.runpytest("--collect-only") + result = testdir.inline_runpytest("--collect-only") result.stdout.fnmatch_lines([ "* 0 assert "hello" not in result.stdout.str() - result = testdir.runpytest("-rw") + result = testdir.inline_runpytest("-rw") result.stdout.fnmatch_lines(""" ===*warning summary*=== *WT1*test_warn_on_test_item*:5*hello* diff --git a/testing/test_nose.py b/testing/test_nose.py index af878da78..b4b3d8836 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -18,10 +18,8 @@ def test_nose_setup(testdir): test_hello.setup = lambda: l.append(1) test_hello.teardown = lambda: l.append(2) """) - result = testdir.runpytest(p, '-p', 'nose') - result.stdout.fnmatch_lines([ - "*2 passed*" - ]) + result = testdir.inline_runpytest(p, '-p', 'nose') + result.assert_outcomes(passed=2) def test_setup_func_with_setup_decorator(): @@ -65,10 +63,8 @@ def test_nose_setup_func(testdir): assert l == [1,2] """) - result = testdir.runpytest(p, '-p', 'nose') - result.stdout.fnmatch_lines([ - "*2 passed*" - ]) + result = testdir.inline_runpytest(p, '-p', 'nose') + result.assert_outcomes(passed=2) def test_nose_setup_func_failure(testdir): @@ -89,7 +85,7 @@ def test_nose_setup_func_failure(testdir): assert l == [1,2] """) - result = testdir.runpytest(p, '-p', 'nose') + result = testdir.inline_runpytest(p, '-p', 'nose') result.stdout.fnmatch_lines([ "*TypeError: ()*" ]) @@ -140,7 +136,7 @@ def test_nose_setup_partial(testdir): test_hello.setup = my_setup_partial test_hello.teardown = my_teardown_partial """) - result = testdir.runpytest(p, '-p', 'nose') + result = testdir.inline_runpytest(p, '-p', 'nose') result.stdout.fnmatch_lines([ "*2 passed*" ]) @@ -207,7 +203,7 @@ def test_nose_test_generator_fixtures(testdir): #expect.append('setup') eq_(self.called, expect) """) - result = testdir.runpytest(p, '-p', 'nose') + result = testdir.inline_runpytest(p, '-p', 'nose') result.stdout.fnmatch_lines([ "*10 passed*" ]) @@ -238,7 +234,7 @@ def test_module_level_setup(testdir): assert items[2] == 2 assert 1 not in items """) - result = testdir.runpytest('-p', 'nose') + result = testdir.inline_runpytest('-p', 'nose') result.stdout.fnmatch_lines([ "*2 passed*", ]) @@ -260,7 +256,7 @@ def test_nose_style_setup_teardown(testdir): def test_world(): assert l == [1] """) - result = testdir.runpytest('-p', 'nose') + result = testdir.inline_runpytest('-p', 'nose') result.stdout.fnmatch_lines([ "*2 passed*", ]) @@ -276,7 +272,7 @@ def test_nose_setup_ordering(testdir): def test_first(self): pass """) - result = testdir.runpytest() + result = testdir.inline_runpytest() result.stdout.fnmatch_lines([ "*1 passed*", ]) @@ -301,8 +297,8 @@ def test_apiwrapper_problem_issue260(testdir): def test_fun(self): pass """) - result = testdir.runpytest() - result.stdout.fnmatch_lines("*1 passed*") + result = testdir.inline_runpytest() + result.assert_outcomes(passed=1) @pytest.mark.skipif("sys.version_info < (2,6)") def test_setup_teardown_linking_issue265(testdir): @@ -327,8 +323,8 @@ def test_setup_teardown_linking_issue265(testdir): """Undoes the setup.""" raise Exception("should not call teardown for skipped tests") ''') - reprec = testdir.inline_run() - reprec.assertoutcome(passed=1, skipped=1) + reprec = testdir.inline_runpytest() + reprec.assert_outcomes(passed=1, skipped=1) def test_SkipTest_during_collection(testdir): @@ -338,8 +334,8 @@ def test_SkipTest_during_collection(testdir): def test_failing(): assert False """) - result = testdir.runpytest(p) - result.assertoutcome(skipped=1) + result = testdir.inline_runpytest(p) + result.assert_outcomes(skipped=1) def test_SkipTest_in_test(testdir):