Skip to content

fix issue 1074 and clean up junitxml - #1091

Merged
RonnyPfannschmidt merged 20 commits into
pytest-dev:masterfrom
RonnyPfannschmidt:fix-1074
Dec 7, 2015
Merged

RonnyPfannschmidt merged 20 commits into
pytest-dev:masterfrom
RonnyPfannschmidt:fix-1074

Conversation

@RonnyPfannschmidt

Copy link
Copy Markdown
Member

i started by running yapf on junitxml to get my ide warning-free
did the same on its tests and will simplify the test api a bit (i want the xml api out of the picture)

Fix #1074
Fix #641

@RonnyPfannschmidt RonnyPfannschmidt changed the title [wip] fix issue 1074 and clean up junitxml fix issue 1074 and clean up junitxml Oct 10, 2015
@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

@alex can you perhaps take a look if that branch has positive effect on the memory usage of the junit reporting of the large testsuite?

@nicoddemus

Copy link
Copy Markdown
Member

Sorry for taking so long to review this @RonnyPfannschmidt! 😅

Overall I really liked the changes you made. As a minor nitpick, I found some PEP-8 errors (mostly too much white-space between functions), but as I said those are minor.

It seems this PR would interest @icemac as well.

Comment thread _pytest/junitxml.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring no longer reflects reality (it doesn't clean properties).

@nicoddemus

Copy link
Copy Markdown
Member

Aside from my minor comments, LGTM. 👍 Feel free to merge if you think my naming suggestions are not worthwhile to make right now. 😄

@icemac

icemac commented Oct 14, 2015

Copy link
Copy Markdown

I will have a look into it, after this PR is merged and released. Thanks for looking into it.

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

@icemac please take a look at it beforehand, i don't have a good benchmark atm, and i'd like to be sure i'm delivering the right thing

@icemac

icemac commented Oct 15, 2015

Copy link
Copy Markdown

@RonnyPfannschmidt I'll test today.

@icemac

icemac commented Oct 15, 2015

Copy link
Copy Markdown

@RonnyPfannschmidt I get an exception after some tests are run (summing up the number of successes etc. in the summary line is less than the number of collected items), see the transcript:

bin/py.test -p no:sugar --junitxml=parts/tests.xml --cov-report html --cov-report xml --cov-report term --cov src
============================= test session starts ==============================
platform linux2 -- Python 2.7.9, pytest-2.8.2.dev1, py-1.4.30, pluggy-0.3.1
rootdir: .../instance, inifile: pytest.ini
plugins: gocept.pytestlayer-2.0, cov-1.8.1, remove-stale-bytecode-1.0
collected 4449 items
src/pytest/doc/en/example/assertion/test_failures.py .
src/pytest/doc/en/example/assertion/test_setup_flow_example.py ..
src/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py .
src/pytest/doc/en/example/costlysetup/sub1/test_quick.py .
src/pytest/doc/en/example/costlysetup/sub2/test_two.py ..
src/pytest/doc/en/example/py2py3/test_py2.py .
src/pytest/testing/test_argcomplete.py ..
src/pytest/testing/test_assertinterpret.py ...........................s
src/pytest/testing/test_assertion.py ..........................................F..F...
src/pytest/testing/test_assertrewrite.py ..........................FF..s........F.....
src/pytest/testing/test_cache.py .................
src/pytest/testing/test_capture.py ...........x.....FFFFF.......F.FF....F.......................................F..F...FFF...
src/pytest/testing/test_collection.py ............x....F.................
src/pytest/testing/test_config.py ........x......................................
src/pytest/testing/test_conftest.py .............F.................................
src/pytest/testing/test_doctest.py ..................................
src/pytest/testing/test_genscript.py s...ss.
src/pytest/testing/test_helpconfig.py .....FF
src/pytest/testing/test_junitxml.py .....F.........................s
src/pytest/testing/test_mark.py ....................................x.................x.
src/pytest/testing/test_monkeypatch.py ..............................
src/pytest/testing/test_nose.py ...................
src/pytest/testing/test_parseopt.py .........................s
src/pytest/testing/test_pastebin.py ...
src/pytest/testing/test_pdb.py ....ssssssssssssFs
src/pytest/testing/test_pluginmanager.py ..F......F................
src/pytest/testing/test_pytester.py x......
src/pytest/testing/test_recwarn.py ...................
src/pytest/testing/test_resultlog.py ...........
src/pytest/testing/test_runner.py ..................sssssss..ssss.......x.........F....
src/pytest/testing/test_runner_xunit.py .............
src/pytest/testing/test_session.py ................
src/pytest/testing/test_skipping.py .................................................
src/pytest/testing/test_terminal.py ..........s.........................s.......................................................
src/pytest/testing/test_tmpdir.py ...........s
src/pytest/testing/test_unittest.py ...................ssssssss...........
src/pytest/testing/cx_freeze/tests/test_doctest.txt .
src/pytest/testing/cx_freeze/tests/test_trivial.py ..
[… many tests of the project under test ran here without any failure …]
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File ".../src/pytest/_pytest/main.py", line 90, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File ".../src/pytest/_pytest/main.py", line 121, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 724, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 596, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File ".../src/pytest/_pytest/main.py", line 146, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 724, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 595, in execute
INTERNALERROR>     return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 253, in _wrapped_call
INTERNALERROR>     return call_outcome.get_result()
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 279, in get_result
INTERNALERROR>     _reraise(*ex)  # noqa
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 264, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 596, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File ".../src/pytest/_pytest/runner.py", line 65, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File ".../src/pytest/_pytest/runner.py", line 72, in runtestprotocol
INTERNALERROR>     rep = call_and_report(item, "setup", log)
INTERNALERROR>   File ".../src/pytest/_pytest/runner.py", line 123, in call_and_report
INTERNALERROR>     hook.pytest_runtest_logreport(report=report)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 724, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File ".../src/pytest/_pytest/vendored_packages/pluggy.py", line 596, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File ".../src/pytest/_pytest/junitxml.py", line 320, in pytest_runtest_logreport
INTERNALERROR>     self.update_testcase_duration(report)
INTERNALERROR>   File ".../src/pytest/_pytest/junitxml.py", line 329, in update_testcase_duration
INTERNALERROR>     reporter.duration += getattr(report, 'duration', 0.0)
INTERNALERROR> AttributeError: '_NodeReporter' object has no attribute 'duration'
28 failed, 2178 passed, 73 skipped, 10 xfailed, 9 pytest-warnings in 400.96 seconds 

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

i see, good find, i'll investigate

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

after investigation i'm certain the issue cannot be replicated when running the correct version of pytest

@nicoddemus @hpk42 this one seems ready for merge

Comment thread _pytest/junitxml.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be node_reporters?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good find

@nicoddemus

Copy link
Copy Markdown
Member

Could be that the problem reported by @icemac be due to _NodeReporter.finalize() being called twice? That would explain the AttributeError.

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

i think its related to the issue about duplicate tests

@nicoddemus

Copy link
Copy Markdown
Member

Good idea. But then it should be simple to verify it, just pass the same file name to pytest twice and you should see the same error. If that's the case we should fix this branch, as we just decided that running the same set of tests more than once is a valid use case.

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

i just tried, and it will err out

@nicoddemus

Copy link
Copy Markdown
Member

The current master will repeat a new testcase tag when you execute the same test twice:

<?xml version="1.0" encoding="utf-8"?>
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="2" time="0.013">
  <testcase classname="tmp.test_foo1" file="tmp\test_foo1.py" line="0" name="test_1" time="0.0010001659393310547"/>
  <testcase classname="tmp.test_foo1" file="tmp\test_foo1.py" line="0" name="test_1" time="0.0010001659393310547"/>
</testsuite>

I was thinking, to support the same case here, perhaps we could:

  • instead of a node_reporters_ordered list, just keep a list of xml strings which have been finished so far (_NodeReporter.to_xml()); lets call that xml_test_cases (just for exemplification, feel free to use a more appropriate name);
  • instead of calling _NodeReporter.finalize() when you see a "teardown" event in pytest_runtest_logreport, just call to_xml(), store it in xml_test_cases and discard the _NodeReporter instance for that nodeid. We won't even need a finalize method anymore.

This will change the order of the testsuite tags in the report, where previously they would appear in the order they were "setup" first, but now they will appear in the order they where "teardown" first, but I don't think this is an issue at all.

What do you think?

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

Will break on xdist

@nicoddemus

Copy link
Copy Markdown
Member

Will break on xdist

You mean in the normal usage, or for duplicated test ids?

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

For duplicate tests, they can interleave wrt reports

@nicoddemus

Copy link
Copy Markdown
Member

Thought about that too, but that happens today already IIUC (both master and this PR)... my proposal would at least avoid the internal error.

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

I plan to fix both by extending the key with the slave ID and implemening your proposal

@nicoddemus

Copy link
Copy Markdown
Member

Sounds good, thanks!

But how will you know which item was executed in which slave? We don't have how to get that information now, I think.

@nicoddemus

Copy link
Copy Markdown
Member

Oooh didn't know about this line:

    def slave_testreport(self, node, rep):
        if rep.when == "call" or (rep.when == "setup" and not rep.passed):
            self.sched.remove_item(node, rep.item_index, rep.duration)
        # self.report_line("testreport %s: %s" %(rep.id, rep.status))
        rep.node = node   # <----
        self.config.hook.pytest_runtest_logreport(report=rep)
        self._handlefailures(rep)

So the report object knows where each test executed, nice. Your suggestion seems perfect then. 😎

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

that little gem came directly from @hpk42 :)

@nicoddemus

Copy link
Copy Markdown
Member

I think this change warrants a CHANGELOG entry

@nicoddemus

Copy link
Copy Markdown
Member

I think after you add a CHANGELOG entry you can merge this @RonnyPfannschmidt

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

im on it

@RonnyPfannschmidt

Copy link
Copy Markdown
Member Author

rebased on to of master and added the entry, i'll wait for travis then push the button

@nicoddemus

Copy link
Copy Markdown
Member

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants