Mailinglist Archive: opensuse-commit (1903 mails)

< Previous Next >
commit python-unittest-xml-reporting for openSUSE:Factory
Hello community,

here is the log from the commit of package python-unittest-xml-reporting for
openSUSE:Factory checked in at 2019-04-14 12:22:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-unittest-xml-reporting (Old)
and /work/SRC/openSUSE:Factory/.python-unittest-xml-reporting.new.27019
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-unittest-xml-reporting"

Sun Apr 14 12:22:58 2019 rev:16 rq:693688 version:2.5.1

Changes:
--------
---
/work/SRC/openSUSE:Factory/python-unittest-xml-reporting/python-unittest-xml-reporting.changes
2019-03-05 12:26:38.888833304 +0100
+++
/work/SRC/openSUSE:Factory/.python-unittest-xml-reporting.new.27019/python-unittest-xml-reporting.changes
2019-04-14 12:23:01.935805035 +0200
@@ -1,0 +2,6 @@
+Fri Apr 12 09:06:13 UTC 2019 - Marketa Calabkova <mcalabkova@xxxxxxxx>
+
+- update to version 2.5.1
+ * no changelog available
+
+-------------------------------------------------------------------

Old:
----
2.2.1.tar.gz

New:
----
2.5.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-unittest-xml-reporting.spec ++++++
--- /var/tmp/diff_new_pack.deHCPU/_old 2019-04-14 12:23:02.655805899 +0200
+++ /var/tmp/diff_new_pack.deHCPU/_new 2019-04-14 12:23:02.655805899 +0200
@@ -18,7 +18,7 @@

%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-unittest-xml-reporting
-Version: 2.2.1
+Version: 2.5.1
Release: 0
Summary: PyUnit-based test runner with JUnit like XML reporting
License: LGPL-3.0-or-later

++++++ 2.2.1.tar.gz -> 2.5.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/.landscape.yml
new/unittest-xml-reporting-2.5.1/.landscape.yml
--- old/unittest-xml-reporting-2.2.1/.landscape.yml 1970-01-01
01:00:00.000000000 +0100
+++ new/unittest-xml-reporting-2.5.1/.landscape.yml 2019-03-25
06:05:10.000000000 +0100
@@ -0,0 +1,8 @@
+doc-warnings: true
+test-warnings: false
+strictness: veryhigh
+max-line-length: 80
+autodetect: true
+python-targets:
+ - 2
+ - 3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/.travis.yml
new/unittest-xml-reporting-2.5.1/.travis.yml
--- old/unittest-xml-reporting-2.2.1/.travis.yml 2019-01-09
07:52:55.000000000 +0100
+++ new/unittest-xml-reporting-2.5.1/.travis.yml 2019-03-25
06:05:10.000000000 +0100
@@ -1,34 +1,53 @@
-sudo: false
-
language: python
+sudo: required
+cache: pip

matrix:
include:
- - python: "2.7"
+ - python: 2.7
env: TOXENV=py27
- - python: "2.7"
+ - python: 2.7
env: TOXENV=py27-djangolts
- - python: "2.7"
+ - python: 2.7
env: TOXENV=py27-djangocurr
- - python: "2.7"
+ - python: 2.7
env: TOXENV=quality
- - python: "3.4"
- env: TOXENV=py34
- - python: "3.5"
+ - python: 3.5
env: TOXENV=py35
- - python: "3.6"
+ - python: 3.6
env: TOXENV=py36
- - python: "3.7-dev"
+ - python: 3.7-dev
env: TOXENV=py37
- - python: "pypy"
+ - python: 3.7-dev
+ env: TOXENV=pytest
+ - os: linux
+ dist: xenial
+ python: pypy2.7-6.0
env: TOXENV=pypy
- - python: "pypy3"
+ services:
+ - docker
+ - os: linux
+ dist: xenial
+ python: pypy3.5-6.0
env: TOXENV=pypy3
+ services:
+ - docker

+before_install:
+ - python --version
+ - uname -a
+ - lsb_release -a
install:
- pip install tox codecov coveralls
+ - virtualenv --version
+ - easy_install --version
+ - pip --version
+ - tox --version
script:
- - tox
+ - tox -v
+after_failure:
+ - more .tox/log/* | cat
+ - more .tox/*/log/* | cat
after_success:
- codecov
- coveralls
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/README.md
new/unittest-xml-reporting-2.5.1/README.md
--- old/unittest-xml-reporting-2.2.1/README.md 2019-01-09 07:52:55.000000000
+0100
+++ new/unittest-xml-reporting-2.5.1/README.md 2019-03-25 06:05:10.000000000
+0100
@@ -4,16 +4,36 @@
[![Documentation
Status](https://readthedocs.org/projects/unittest-xml-reporting/badge/?version=latest)](http://unittest-xml-reporting.readthedocs.io/en/latest/?badge=latest)

[![Build
Status](https://travis-ci.org/xmlrunner/unittest-xml-reporting.svg?branch=master)](https://travis-ci.org/xmlrunner/unittest-xml-reporting)
-[![Code
Health](https://landscape.io/github/xmlrunner/unittest-xml-reporting/master/landscape.png)](https://landscape.io/github/xmlrunner/unittest-xml-reporting/master)
+[![Code
Health](https://landscape.io/github/xmlrunner/unittest-xml-reporting/master/landscape.svg?style=flat)](https://landscape.io/github/xmlrunner/unittest-xml-reporting/master)
[![codecov.io Coverage
Status](https://codecov.io/github/xmlrunner/unittest-xml-reporting/coverage.svg?branch=master)](https://codecov.io/github/xmlrunner/unittest-xml-reporting?branch=master)
[![Coveralls Coverage
Status](https://coveralls.io/repos/xmlrunner/unittest-xml-reporting/badge.svg?branch=master&service=github)](https://coveralls.io/github/xmlrunner/unittest-xml-reporting?branch=master)
[![Requirements
Status](https://requires.io/github/xmlrunner/unittest-xml-reporting/requirements.svg?branch=master)](https://requires.io/github/xmlrunner/unittest-xml-reporting/requirements/?branch=master)

-# unittest-xml-reporting
+# unittest-xml-reporting (aka xmlrunner)

-unittest-xml-reporting is a unittest test runner that can save test results
-to XML files that can be consumed by a wide range of tools, such as build
-systems, IDEs and continuous integration servers.
+A unittest test runner that can save test results to XML files in xUnit format.
+The files can be consumed by a wide range of tools, such as build systems, IDEs
+and continuous integration servers.
+
+## Schema
+
+There are many schemas with minor differences.
+We use one that is compatible with Jenkins xUnit plugin, a copy is
+available under `tests/vendor/jenkins/xunit-plugin/junit-10.xsd` (see attached
license).
+You may also find these resources useful:
+
+-
https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
+- https://stackoverflow.com/questions/11241781/python-unittests-in-jenkins
+- [Jenkins
(junit-10.xsd)](https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd)
+- [JUnit-Schema
(JUnit.xsd)](https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd)
+- [Windyroad (JUnit.xsd)](http://windyroad.com.au/dl/Open%20Source/JUnit.xsd)
+- [a gist (Jenkins xUnit test result
schema)](https://gist.github.com/erikd/4192748)
+
+## Things that are somewhat broken
+
+Python 3 has the concept of sub-tests for a `unittest.TestCase`; this doesn't
map well to an existing
+xUnit concept, so you won't find it in the schema. What that means, is that
you lose some granularity
+in the reports for sub-tests.

## Requirements

@@ -50,6 +70,21 @@
[PyPI](https://pypi.python.org/pypi/unittest-xml-reporting/).


+## Command-line
+
+````bash
+python -m xmlrunner [options]
+python -m xmlrunner discover [options]
+
+# help
+python -m xmlrunner -h
+````
+
+e.g.
+````bash
+python -m xmlrunner discover -t ~/mycode/tests -o /tmp/build/junit-reports
+````
+
## Usage

The script below, adapted from the
@@ -136,7 +171,7 @@
xmlrunner.XMLTestRunner().run(suite)
````

-### Django
+### Django support

In order to plug `XMLTestRunner` to a Django project, add the following
to your `settings.py`:
@@ -147,32 +182,12 @@

Also, the following settings are provided so you can fine tune the reports:

-**TEST_OUTPUT_VERBOSE** (Default: `1`, choose between `0`,`1`, and `2`)
-
-Besides the XML reports generated by the test runner, a bunch of useful
-information is printed to the `sys.stderr` stream, just like the
-`TextTestRunner` does. Use this setting to choose between a verbose and a
-non-verbose output.
-
-**TEST_OUTPUT_DESCRIPTIONS** (Default: `False`)
-
-If your test methods contains docstrings, you can display such docstrings
-instead of display the test name (ex: `module.TestCase.test_method`). In
-order to use this feature, you have to enable verbose output by setting
-`TEST_OUTPUT_VERBOSE = 2`.
-
-**TEST_OUTPUT_DIR** (Default: `"."`)
-
-Tells the test runner where to put the XML reports. If the directory
-couldn't be found, the test runner will try to create it before
-generate the XML files.
-
-**TEST_OUTPUT_FILE_NAME** (Default: `None`)
-
-Tells the test runner to output a single XML report with this filename
-under `os.path.join(TEST_OUTPUT_DIR, TEST_OUTPUT_FILE_NAME)`. Please note
-that for long running tests, this will keep the results in memory for
-a longer time than multiple reports, and may use up more resources.
+|setting|default|values|description|
+|-|-|-|-|
+|`TEST_OUTPUT_VERBOSE`|`1`|`0\|1\|2`|Besides the XML reports generated by the
test runner, a bunch of useful information is printed to the `sys.stderr`
stream, just like the `TextTestRunner` does. Use this setting to choose between
a verbose and a non-verbose output.|
+|`TEST_OUTPUT_DESCRIPTIONS`|`False`|`True\|False`|If your test methods
contains docstrings, you can display such docstrings instead of display the
test name (ex: `module.TestCase.test_method`).<br>In order to use this feature,
you have to enable verbose output by setting `TEST_OUTPUT_VERBOSE = 2`.<br>Only
effects stdout and not XML output.|
+|`TEST_OUTPUT_DIR`|`"."`|`<str>`|Tells the test runner where to put the XML
reports. If the directory couldn't be found, the test runner will try to create
it before generate the XML files.|
+|`TEST_OUTPUT_FILE_NAME`|`None`|`<str>`|Tells the test runner to output a
single XML report with this filename under `os.path.join(TEST_OUTPUT_DIR,
TEST_OUTPUT_FILE_NAME)`.<br>Please note that for long running tests, this will
keep the results in memory for a longer time than multiple reports, and may use
up more resources.|


## Contributing
@@ -191,6 +206,11 @@

```bash
$ pip install tox
+
+# basic sanity test, friendly output
+$ tox -e pytest
+
+# all combinations
$ tox
```

diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/setup.py
new/unittest-xml-reporting-2.5.1/setup.py
--- old/unittest-xml-reporting-2.2.1/setup.py 2019-01-09 07:52:55.000000000
+0100
+++ new/unittest-xml-reporting-2.5.1/setup.py 2019-03-25 06:05:10.000000000
+0100
@@ -10,6 +10,11 @@
with codecs.open(ver_path, 'rb', 'utf8') as ver_file:
exec(ver_file.read(), main_ns)

+# Load README.md
+readme_path = convert_path('README.md')
+with codecs.open(readme_path, 'rb', 'utf8') as readme_file:
+ long_description = readme_file.read()
+
install_requires = ['six>=1.4.0']

# this is for sdist to work.
@@ -21,13 +26,14 @@
setup(
name = 'unittest-xml-reporting',
version = main_ns['__version__'],
- author = 'Daniel Fernandes Martins',
- author_email = 'daniel.tritone@xxxxxxxxx',
+ author = 'Daniel Fernandes Martins, Damien Nozay',
description = 'unittest-based test runner with Ant/JUnit like XML
reporting.',
+ long_description = long_description,
+ long_description_content_type = 'text/markdown',
license = 'BSD',
platforms = ['Any'],
keywords = [
- 'pyunit', 'unittest', 'junit xml', 'report', 'testrunner', 'xmlrunner'
+ 'pyunit', 'unittest', 'junit xml', 'xunit', 'report', 'testrunner',
'xmlrunner'
],
url = 'http://github.com/xmlrunner/unittest-xml-reporting/tree/master/',
classifiers = [
@@ -40,10 +46,9 @@
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.3',
- 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Libraries :: Python Modules',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/tests/testsuite.py
new/unittest-xml-reporting-2.5.1/tests/testsuite.py
--- old/unittest-xml-reporting-2.2.1/tests/testsuite.py 2019-01-09
07:52:55.000000000 +0100
+++ new/unittest-xml-reporting-2.5.1/tests/testsuite.py 2019-03-25
06:05:10.000000000 +0100
@@ -3,6 +3,8 @@

"""Executable module to test unittest-xml-reporting.
"""
+from __future__ import print_function
+
import contextlib
import io
import sys
@@ -48,6 +50,20 @@
JUnitSchema.assertValid(document)


+class TestCaseSubclassWithNoSuper(unittest.TestCase):
+ def __init__(self, description):
+ # no super, see #189
+ pass
+
+ def run(self, result):
+ result = _XMLTestResult()
+ result.startTest(self)
+ result.stopTest(self)
+
+ def test_something(self):
+ pass
+
+
class DoctestTest(unittest.TestCase):

def test_doctest_example(self):
@@ -59,10 +75,10 @@
runner.run(suite)
outdir.seek(0)
output = outdir.read()
- self.assertIn('classname="tests.doctest_example.Multiplicator" '
- 'name="threetimes"'.encode('utf8'), output)
- self.assertIn('classname="tests.doctest_example" '
- 'name="twice"'.encode('utf8'), output)
+
self.assertIn('classname="tests.doctest_example.Multiplicator"'.encode('utf8'),
output)
+ self.assertIn('name="threetimes"'.encode('utf8'), output)
+ self.assertIn('classname="tests.doctest_example"'.encode('utf8'),
output)
+ self.assertIn('name="twice"'.encode('utf8'), output)


@contextlib.contextmanager
@@ -81,6 +97,16 @@
sys.stderr = orig_stderr


+def _strip_xml(xml, changes):
+ doc = etree.fromstring(xml)
+ for xpath, attributes in changes.items():
+ for node in doc.xpath(xpath):
+ for attrib in node.attrib.keys():
+ if attrib not in attributes:
+ del node.attrib[attrib]
+ return etree.tostring(doc)
+
+
class XMLTestRunnerTestCase(unittest.TestCase):
"""
XMLTestRunner test case.
@@ -121,6 +147,10 @@
def test_unsafe_unicode(self):
print(u"A\x00B\x08C\x0BD\x0C")

+ def test_output_stdout_and_stderr(self):
+ print('test on stdout')
+ print('test on stderr', file=sys.stderr)
+
def test_runner_buffer_output_pass(self):
print('should not be printed')

@@ -152,6 +182,11 @@
with self.subTest(i=i):
raise Exception('this is a subtest')

+ def test_subTest_mixed(self):
+ for i in range(2):
+ with self.subTest(i=i):
+ self.assertLess(i, 1, msg='this is a subtest.')
+
class DummyErrorInCallTest(unittest.TestCase):

def __call__(self, result):
@@ -179,8 +214,9 @@
self.runner_kwargs = {}
self.addCleanup(rmtree, self.outdir)

- def _test_xmlrunner(self, suite, runner=None):
- outdir = self.outdir
+ def _test_xmlrunner(self, suite, runner=None, outdir=None):
+ if outdir is None:
+ outdir = self.outdir
stream = self.stream
verbosity = self.verbosity
runner_kwargs = self.runner_kwargs
@@ -188,9 +224,15 @@
runner = xmlrunner.XMLTestRunner(
stream=stream, output=outdir, verbosity=verbosity,
**runner_kwargs)
- self.assertEqual(0, len(glob(os.path.join(outdir, '*xml'))))
+ if isinstance(outdir, BytesIO):
+ self.assertFalse(outdir.getvalue())
+ else:
+ self.assertEqual(0, len(glob(os.path.join(outdir, '*xml'))))
runner.run(suite)
- self.assertEqual(1, len(glob(os.path.join(outdir, '*xml'))))
+ if isinstance(outdir, BytesIO):
+ self.assertTrue(outdir.getvalue())
+ else:
+ self.assertEqual(1, len(glob(os.path.join(outdir, '*xml'))))
return runner

def test_basic_unittest_constructs(self):
@@ -214,6 +256,11 @@
runner.run(suite)
outdir.seek(0)
output = outdir.read()
+ output = _strip_xml(output, {
+ '//testsuite': (),
+ '//testcase': ('classname', 'name'),
+ '//failure': ('message',),
+ })
self.assertRegexpMatches(
output,
r'classname="tests\.testsuite\.(XMLTestRunnerTestCase\.)?'
@@ -222,7 +269,30 @@
self.assertRegexpMatches(
output,
r'classname="tests\.testsuite\.(XMLTestRunnerTestCase\.)?'
- r'DummySubTest" name="test_subTest_pass"'.encode('utf8'))
+ r'DummySubTest" name="test_subTest_pass"'.encode('utf8'),
+ )
+
+ def test_expected_failure(self):
+ suite = unittest.TestSuite()
+ suite.addTest(self.DummyTest('test_expected_failure'))
+ outdir = BytesIO()
+
+ self._test_xmlrunner(suite, outdir=outdir)
+
+ self.assertNotIn(b'<failure', outdir.getvalue())
+ self.assertNotIn(b'<error', outdir.getvalue())
+ self.assertIn(b'<skip', outdir.getvalue())
+
+ def test_unexpected_success(self):
+ suite = unittest.TestSuite()
+ suite.addTest(self.DummyTest('test_unexpected_success'))
+ outdir = BytesIO()
+
+ self._test_xmlrunner(suite, outdir=outdir)
+
+ self.assertNotIn(b'<failure', outdir.getvalue())
+ self.assertIn(b'<error', outdir.getvalue())
+ self.assertNotIn(b'<skip', outdir.getvalue())

def test_xmlrunner_non_ascii(self):
suite = unittest.TestSuite()
@@ -410,6 +480,11 @@
runner.run(suite)
outdir.seek(0)
output = outdir.read()
+ output = _strip_xml(output, {
+ '//testsuite': (),
+ '//testcase': ('classname', 'name'),
+ '//failure': ('message',),
+ })
self.assertRegexpMatches(
output,
br'<testcase classname="tests\.testsuite\.'
@@ -434,6 +509,11 @@
runner.run(suite)
outdir.seek(0)
output = outdir.read()
+ output = _strip_xml(output, {
+ '//testsuite': (),
+ '//testcase': ('classname', 'name'),
+ '//failure': ('message',),
+ })
self.assertRegexpMatches(
output,
br'<testcase classname="tests\.testsuite\.'
@@ -445,6 +525,32 @@
br'(XMLTestRunnerTestCase\.)?DummySubTest" '
br'name="test_subTest_error \(i=1\)"')

+
+ @unittest.skipIf(not hasattr(unittest.TestCase, 'subTest'),
+ 'unittest.TestCase.subTest not present.')
+ def test_unittest_subTest_mixed(self):
+ # test for issue #155
+ outdir = BytesIO()
+ runner = xmlrunner.XMLTestRunner(
+ stream=self.stream, output=outdir, verbosity=self.verbosity,
+ **self.runner_kwargs)
+ suite = unittest.TestSuite()
+ suite.addTest(self.DummySubTest('test_subTest_mixed'))
+ runner.run(suite)
+ outdir.seek(0)
+ output = outdir.read()
+ output = _strip_xml(output, {
+ '//testsuite': (),
+ '//testcase': ('classname', 'name'),
+ '//failure': ('message',),
+ })
+ self.assertNotIn(
+ 'name="test_subTest_mixed (i=0)"'.encode('utf8'),
+ output)
+ self.assertIn(
+ 'name="test_subTest_mixed (i=1)"'.encode('utf8'),
+ output)
+
@unittest.skipIf(not hasattr(unittest.TestCase, 'subTest'),
'unittest.TestCase.subTest not present.')
def test_unittest_subTest_pass(self):
@@ -517,6 +623,7 @@
suite = unittest.TestSuite()
suite.addTest(self.DummyTest('test_fail'))
suite.addTest(self.DummyTest('test_pass'))
+ suite.addTest(self.DummyTest('test_output_stdout_and_stderr'))
suite.properties = dict(key='value')
outdir = BytesIO()
runner = xmlrunner.XMLTestRunner(
@@ -575,6 +682,15 @@
suite.addTest(self.DummyTest('test_pass'))
runner.run(suite)

+ def test_xmlrunner_stream_empty_testsuite(self):
+ stream = self.stream
+ output = BytesIO()
+ runner = xmlrunner.XMLTestRunner(
+ stream=stream, output=output, verbosity=self.verbosity,
+ **self.runner_kwargs)
+ suite = unittest.TestSuite()
+ runner.run(suite)
+
def test_xmlrunner_output_subdir(self):
stream = self.stream
output = os.path.join(self.outdir, 'subdir')
@@ -689,6 +805,25 @@
self.assertIn('should be printed', r[0].getvalue())
self.assertNotIn('should be printed', r[1].getvalue())

+ def test_partialmethod(self):
+ try:
+ from functools import partialmethod
+ except ImportError:
+ raise unittest.SkipTest('functools.partialmethod is not
available.')
+ def test_partialmethod(test):
+ pass
+ class TestWithPartialmethod(unittest.TestCase):
+ pass
+ setattr(
+ TestWithPartialmethod,
+ 'test_partialmethod',
+ partialmethod(test_partialmethod),
+ )
+ suite = unittest.TestSuite()
+ suite.addTest(TestWithPartialmethod('test_partialmethod'))
+ self._test_xmlrunner(suite)
+
+

class DuplicateWriterTestCase(unittest.TestCase):
def setUp(self):
@@ -735,7 +870,6 @@
self.assertEqual(wrote, len(self.getSecondContent()))


-@unittest.skipIf(sys.version_info[0] < 3, 'Python 3 required')
class XMLProgramTestCase(unittest.TestCase):
@mock.patch('sys.argv', ['xmlrunner', '-o', 'flaf'])
@mock.patch('xmlrunner.runner.XMLTestRunner')
@@ -744,16 +878,39 @@
xmlrunner.runner.XMLTestProgram()

kwargs = dict(
- buffer=False,
- failfast=False,
- verbosity=1,
- warnings='default',
+ buffer=mock.ANY,
+ failfast=mock.ANY,
+ verbosity=mock.ANY,
+ warnings=mock.ANY,
output='flaf',
)

if sys.version_info[:2] > (3, 4):
- kwargs.update(tb_locals=False)
+ kwargs.update(tb_locals=mock.ANY)

testrunner.assert_called_once_with(**kwargs)
+ exiter.assert_called_once_with(False)

+ @mock.patch('sys.argv', ['xmlrunner', '--output-file', 'test.xml'])
+ @mock.patch('xmlrunner.runner.open')
+ @mock.patch('xmlrunner.runner.XMLTestRunner')
+ @mock.patch('sys.exit')
+ def test_xmlrunner_output_file(self, exiter, testrunner, opener):
+ xmlrunner.runner.XMLTestProgram()
+ opener.assert_called_once_with('test.xml', 'wb')
+ open_file = opener()
+ open_file.close.assert_called_with()
+
+ kwargs = dict(
+ buffer=mock.ANY,
+ failfast=mock.ANY,
+ verbosity=mock.ANY,
+ warnings=mock.ANY,
+ output=open_file,
+ )
+
+ if sys.version_info[:2] > (3, 4):
+ kwargs.update(tb_locals=mock.ANY)
+
+ testrunner.assert_called_once_with(**kwargs)
exiter.assert_called_once_with(False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/tox.ini
new/unittest-xml-reporting-2.5.1/tox.ini
--- old/unittest-xml-reporting-2.2.1/tox.ini 2019-01-09 07:52:55.000000000
+0100
+++ new/unittest-xml-reporting-2.5.1/tox.ini 2019-03-25 06:05:10.000000000
+0100
@@ -1,29 +1,38 @@
+[pytest]
+python_files = *_test.py test*.py
+testpaths = tests
+norecursedirs = tests/django_example
+
[tox]
-envlist = begin,py{27,py,py3,34,35,36,37},py27-django{lts,curr},end,quality
+envlist = begin,py{27,py,py3,35,36,37},pytest,py27-django{lts,curr},end,quality

[tox:travis]
2.7 = begin,py27,py27-django{lts,curr},end,quality
-3.4 = py34
3.5 = py35
3.6 = py36
-3.7 = py37
+3.7 = py37,pytest

[testenv]
deps =
coverage
codecov>=1.4.0
coveralls
- djangolts: django>=1.8.8,<1.9.0
+ djangolts,pytest: django>=1.8.8,<1.9.0
djangocurr: django>=1.9.1
+ pytest: pytest
lxml>=3.6.0
mock
commands =
coverage run --append setup.py test
coverage report --omit='.tox/*'
+ python -m xmlrunner discover -p test_xmlrunner_output
codecov -e TOXENV
-coveralls
passenv = CI TRAVIS_BUILD_ID TRAVIS TRAVIS_BRANCH TRAVIS_JOB_NUMBER
TRAVIS_PULL_REQUEST TRAVIS_JOB_ID TRAVIS_REPO_SLUG TRAVIS_COMMIT

+[testenv:pytest]
+commands = pytest
+
[testenv:begin]
commands = coverage erase

diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/xmlrunner/result.py
new/unittest-xml-reporting-2.5.1/xmlrunner/result.py
--- old/unittest-xml-reporting-2.2.1/xmlrunner/result.py 2019-01-09
07:52:55.000000000 +0100
+++ new/unittest-xml-reporting-2.5.1/xmlrunner/result.py 2019-03-25
06:05:10.000000000 +0100
@@ -1,4 +1,5 @@

+import inspect
import io
import os
import sys
@@ -10,6 +11,9 @@
from os import path
from six.moves import StringIO

+# use direct import to bypass freezegun
+from time import time
+
from .unittest import TestResult, _TextTestResult, failfast


@@ -46,18 +50,7 @@
STDERR_LINE = '\nStderr:\n%s'


-def xml_safe_unicode(base, encoding='utf-8'):
- """Return a unicode string containing only valid XML characters.
-
- encoding - if base is a byte string it is first decoded to unicode
- using this encoding.
- """
- if isinstance(base, six.binary_type):
- base = base.decode(encoding)
- return INVALID_XML_1_0_UNICODE_RE.sub('', base)
-
-
-def to_unicode(data):
+def _to_unicode(data):
"""Returns unicode in Python2 and str in Python3"""
if six.PY3:
return six.text_type(data)
@@ -68,8 +61,17 @@
return repr(data).decode('utf8', 'replace')


-def safe_unicode(data, encoding=None):
- return xml_safe_unicode(to_unicode(data), encoding)
+def safe_unicode(data, encoding='utf8'):
+ """Return a unicode string containing only valid XML characters.
+
+ encoding - if data is a byte string it is first decoded to unicode
+ using this encoding.
+ """
+ data = _to_unicode(data)
+ if isinstance(data, six.binary_type):
+ # e.g. IronPython, see #182
+ data = data.decode(encoding)
+ return INVALID_XML_1_0_UNICODE_RE.sub('', data)


def testcase_name(test_method):
@@ -132,7 +134,14 @@
# Possible test outcomes
(SUCCESS, FAILURE, ERROR, SKIP) = range(4)

- def __init__(self, test_result, test_method, outcome=SUCCESS, err=None,
subTest=None):
+ OUTCOME_ELEMENTS = {
+ SUCCESS: None,
+ FAILURE: 'failure',
+ ERROR: 'error',
+ SKIP: 'skipped',
+ }
+
+ def __init__(self, test_result, test_method, outcome=SUCCESS, err=None,
subTest=None, filename=None, lineno=None):
self.test_result = test_result
self.outcome = outcome
self.elapsed_time = 0
@@ -156,10 +165,13 @@

self.test_name = testcase_name(test_method)
self.test_id = test_method.id()
- self.subDescription = None
+
if subTest:
self.test_id = subTest.id()
- self.subDescription = subTest._subDescription()
+ self.test_description = self.test_result.getDescription(subTest)
+
+ self.filename = filename
+ self.lineno = lineno

def id(self):
return self.test_id
@@ -172,17 +184,6 @@
timestamp = datetime.datetime.fromtimestamp(self.test_result.stop_time)
self.timestamp = timestamp.replace(microsecond=0).isoformat()

- def get_description(self):
- """
- Return a text representation of the test method.
- """
- description = self.test_description
-
- if self.subDescription is not None:
- description += ' ' + self.subDescription
-
- return description
-
def get_error_info(self):
"""
Return a text representation of an exception thrown by a test
@@ -210,6 +211,8 @@
self.callback = None
self.elapsed_times = elapsed_times
self.properties = properties # junit testsuite properties
+ self.filename = None
+ self.lineno = None
if infoclass is None:
self.infoclass = _TestInfo
else:
@@ -221,6 +224,8 @@
Appends a `infoclass` to the given target list and sets a callback
method to be called by stopTest method.
"""
+ test_info.filename = self.filename
+ test_info.lineno = self.lineno
target_list.append(test_info)

def callback():
@@ -249,9 +254,27 @@
"""
Called before execute each test method.
"""
- self.start_time = time.time()
+ self.start_time = time()
TestResult.startTest(self, test)

+ try:
+ if getattr(test, '_dt_test', None) is not None:
+ # doctest.DocTestCase
+ self.filename = test._dt_test.filename
+ self.lineno = test._dt_test.lineno
+ else:
+ # regular unittest.TestCase?
+ test_method = getattr(test, test._testMethodName)
+ test_class = type(test)
+ # Note: inspect can get confused with decorators, so use class.
+ self.filename = inspect.getsourcefile(test_class)
+ # Handle partial and partialmethod objects.
+ test_method = getattr(test_method, 'func', test_method)
+ _, self.lineno = inspect.getsourcelines(test_method)
+ except (AttributeError, TypeError):
+ # issue #188, #189, some frameworks can make test method opaque.
+ pass
+
if self.showAll:
self.stream.write(' ' + self.getDescription(test))
self.stream.write(" ... ")
@@ -296,7 +319,7 @@
# self._stderr_data = sys.stderr.getvalue()

_TextTestResult.stopTest(self, test)
- self.stop_time = time.time()
+ self.stop_time = time()

if self.callback and callable(self.callback):
self.callback()
@@ -308,7 +331,7 @@
"""
self._save_output_data()
self._prepare_callback(
- self.infoclass(self, test), self.successes, 'OK', '.'
+ self.infoclass(self, test), self.successes, 'ok', '.'
)

@failfast
@@ -375,8 +398,10 @@
self._save_output_data()
testinfo = self.infoclass(
self, test, self.infoclass.SKIP, reason)
+ testinfo.test_exception_name = 'skip'
+ testinfo.test_exception_message = reason
self.skipped.append((testinfo, reason))
- self._prepare_callback(testinfo, [], 'SKIP', 'S')
+ self._prepare_callback(testinfo, [], 'skip', 's')

def addExpectedFailure(self, test, err):
"""
@@ -384,12 +409,12 @@
"""
self._save_output_data()

- testinfo = self.infoclass(self, test, self.infoclass.ERROR, err)
- testinfo.test_exception_name = 'ExpectedFailure'
- testinfo.test_exception_message = 'EXPECTED FAILURE:
{}'.format(testinfo.test_exception_message)
+ testinfo = self.infoclass(self, test, self.infoclass.SKIP, err)
+ testinfo.test_exception_name = 'XFAIL'
+ testinfo.test_exception_message = 'expected failure:
{}'.format(testinfo.test_exception_message)

self.expectedFailures.append((testinfo, self._exc_info_to_string(err,
test)))
- self._prepare_callback(testinfo, [], 'EXPECTED FAILURE', 'X')
+ self._prepare_callback(testinfo, [], 'expected failure', 'x')

@failfast
def addUnexpectedSuccess(self, test):
@@ -402,11 +427,11 @@
testinfo.outcome = self.infoclass.ERROR
# But since we want to have error outcome, we need to provide
additional fields:
testinfo.test_exception_name = 'UnexpectedSuccess'
- testinfo.test_exception_message = ('UNEXPECTED SUCCESS: This test was
marked as expected failure but passed, '
+ testinfo.test_exception_message = ('Unexpected success: This test was
marked as expected failure but passed, '
'please review it')

- self.unexpectedSuccesses.append(testinfo)
- self._prepare_callback(testinfo, [], 'UNEXPECTED SUCCESS', 'U')
+ self.unexpectedSuccesses.append((testinfo, 'unexpected success'))
+ self._prepare_callback(testinfo, [], 'unexpected success', 'u')

def printErrorList(self, flavour, errors):
"""
@@ -416,7 +441,7 @@
self.stream.writeln(self.separator1)
self.stream.writeln(
'%s [%.3fs]: %s' % (flavour, test_info.elapsed_time,
- test_info.get_description())
+ test_info.test_description)
)
self.stream.writeln(self.separator2)
self.stream.writeln('%s' % test_info.get_error_info())
@@ -431,7 +456,7 @@
tests_by_testcase = {}

for tests in (self.successes, self.failures, self.errors,
- self.skipped):
+ self.skipped, self.expectedFailures,
self.unexpectedSuccesses):
for test_info in tests:
if isinstance(test_info, tuple):
# This is a skipped, error or a failure test case
@@ -462,9 +487,12 @@
"""
testsuite = xml_document.createElement('testsuite')
parentElement.appendChild(testsuite)
+ module_name = suite_name.rpartition('.')[0]
+ file_name = module_name.replace('.', '/') + '.py'

testsuite.setAttribute('name', suite_name)
testsuite.setAttribute('tests', str(len(tests)))
+ testsuite.setAttribute('file', file_name)

testsuite.setAttribute(
'time', '%.3f' % sum(map(lambda e: e.elapsed_time, tests))
@@ -488,28 +516,6 @@
for test in tests:
_XMLTestResult._report_testcase(test, testsuite, xml_document)

- systemout = xml_document.createElement('system-out')
- testsuite.appendChild(systemout)
-
- stdout = StringIO()
- for test in tests:
- # Merge the stdout from the tests in a class
- if test.stdout is not None:
- stdout.write(test.stdout)
- _XMLTestResult._createCDATAsections(
- xml_document, systemout, stdout.getvalue())
-
- systemerr = xml_document.createElement('system-err')
- testsuite.appendChild(systemerr)
-
- stderr = StringIO()
- for test in tests:
- # Merge the stderr from the tests in a class
- if test.stderr is not None:
- stderr.write(test.stderr)
- _XMLTestResult._createCDATAsections(
- xml_document, systemerr, stderr.getvalue())
-
return testsuite

_report_testsuite = staticmethod(_report_testsuite)
@@ -552,25 +558,45 @@
testcase.setAttribute('time', '%.3f' % test_result.elapsed_time)
testcase.setAttribute('timestamp', test_result.timestamp)

- if (test_result.outcome != test_result.SUCCESS):
- elem_name = ('failure', 'error', 'skipped')[test_result.outcome-1]
- failure = xml_document.createElement(elem_name)
- testcase.appendChild(failure)
- if test_result.outcome != test_result.SKIP:
- failure.setAttribute(
- 'type',
- test_result.test_exception_name
- )
- failure.setAttribute(
- 'message',
- test_result.test_exception_message
- )
+ if test_result.filename is not None:
+ # Try to make filename relative to current directory.
+ filename = os.path.relpath(test_result.filename)
+ filename = test_result.filename if filename.startswith('../') else
filename
+ testcase.setAttribute('file', filename)
+
+ if test_result.lineno is not None:
+ testcase.setAttribute('line', str(test_result.lineno))
+
+ result_elem_name = test_result.OUTCOME_ELEMENTS[test_result.outcome]
+
+ if result_elem_name is not None:
+ result_elem = xml_document.createElement(result_elem_name)
+ testcase.appendChild(result_elem)
+
+ result_elem.setAttribute(
+ 'type',
+ test_result.test_exception_name
+ )
+ result_elem.setAttribute(
+ 'message',
+ test_result.test_exception_message
+ )
+ if test_result.get_error_info():
error_info = safe_unicode(test_result.get_error_info())
_XMLTestResult._createCDATAsections(
- xml_document, failure, error_info)
- else:
- failure.setAttribute('type', 'skip')
- failure.setAttribute('message',
test_result.test_exception_message)
+ xml_document, result_elem, error_info)
+
+ if test_result.stdout is not None:
+ systemout = xml_document.createElement('system-out')
+ testcase.appendChild(systemout)
+ _XMLTestResult._createCDATAsections(
+ xml_document, systemout, test_result.stdout)
+
+ if test_result.stderr is not None:
+ systemout = xml_document.createElement('system-err')
+ testcase.appendChild(systemout)
+ _XMLTestResult._createCDATAsections(
+ xml_document, systemout, test_result.stderr)

_report_testcase = staticmethod(_report_testcase)

@@ -607,22 +633,27 @@
testsuite = _XMLTestResult._report_testsuite(
suite_name, tests, doc, parentElement, self.properties
)
- xml_content = doc.toprettyxml(
- indent='\t',
- encoding=test_runner.encoding
- )

if outputHandledAsString:
+ xml_content = doc.toprettyxml(
+ indent='\t',
+ encoding=test_runner.encoding
+ )
filename = path.join(
test_runner.output,
'TEST-%s.xml' % suite_name)
with open(filename, 'wb') as report_file:
report_file.write(xml_content)

- self.stream.writeln('Generated XML report:
{}'.format(filename))
+ if self.showAll:
+ self.stream.writeln('Generated XML report:
{}'.format(filename))

if not outputHandledAsString:
# Assume that test_runner.output is a stream
+ xml_content = doc.toprettyxml(
+ indent='\t',
+ encoding=test_runner.encoding
+ )
test_runner.output.write(xml_content)

def _exc_info_to_string(self, err, test):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/xmlrunner/runner.py
new/unittest-xml-reporting-2.5.1/xmlrunner/runner.py
--- old/unittest-xml-reporting-2.2.1/xmlrunner/runner.py 2019-01-09
07:52:55.000000000 +0100
+++ new/unittest-xml-reporting-2.5.1/xmlrunner/runner.py 2019-03-25
06:05:10.000000000 +0100
@@ -1,7 +1,10 @@

+import argparse
import sys
import time

+import six
+
from .unittest import TextTestRunner, TestProgram
from .result import _XMLTestResult

@@ -16,8 +19,10 @@
"""
def __init__(self, output='.', outsuffix=None,
elapsed_times=True, encoding=UTF8,
- resultclass=None,
+ resultclass=None, warnings=None,
**kwargs):
+ if six.PY3:
+ kwargs['warnings'] = warnings
super(XMLTestRunner, self).__init__(**kwargs)
self.output = output
self.encoding = encoding
@@ -116,31 +121,65 @@


class XMLTestProgram(TestProgram):
- output = None

def __init__(self, *args, **kwargs):
kwargs.setdefault('testRunner', XMLTestRunner)
+ self.warnings = None # python2 fix
+ self._parseKnownArgs(kwargs)
super(XMLTestProgram, self).__init__(*args, **kwargs)

+ def _parseKnownArgs(self, kwargs):
+ argv = kwargs.get('argv')
+ if argv is None:
+ argv = sys.argv
+
+ # python2 argparse fix
+ parser = argparse.ArgumentParser(prog='xmlrunner')
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument(
+ '-o', '--output', metavar='DIR',
+ help='Directory for storing XML reports (\'.\' default)')
+ group.add_argument(
+ '--output-file', metavar='FILENAME',
+ help='Filename for storing XML report')
+ namespace, argv = parser.parse_known_args(argv)
+ self.output = namespace.output
+ self.output_file = namespace.output_file
+ kwargs['argv'] = argv
+
def _initArgParsers(self):
+ # this code path is only called in python3 (optparse vs argparse)
super(XMLTestProgram, self)._initArgParsers()

for parser in (self._main_parser, self._discovery_parser):
- parser.add_argument('-o', '--output', metavar='DIR',
- help='Directory for storing XML reports '
- "('.' default)")
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument(
+ '-o', '--output', metavar='DIR', nargs=1,
+ help='Directory for storing XML reports (\'.\' default)')
+ group.add_argument(
+ '--output-file', metavar='FILENAME', nargs=1,
+ help='Filename for storing XML report')

def runTests(self):
- if self.output is not None:
- kwargs = dict(verbosity=self.verbosity,
- failfast=self.failfast,
- buffer=self.buffer,
- warnings=self.warnings,
- output=self.output)
+ kwargs = dict(
+ verbosity=self.verbosity,
+ failfast=self.failfast,
+ buffer=self.buffer,
+ warnings=self.warnings,
+ )
+ if sys.version_info[:2] > (3, 4):
+ kwargs.update(tb_locals=self.tb_locals)

- if sys.version_info[:2] > (3, 4):
- kwargs.update(tb_locals=self.tb_locals)
+ output_file = None
+ try:
+ if self.output_file is not None:
+ output_file = open(self.output_file, 'wb')
+ kwargs.update(output=output_file)
+ elif self.output is not None:
+ kwargs.update(output=self.output)

self.testRunner = self.testRunner(**kwargs)
-
- super(XMLTestProgram, self).runTests()
+ super(XMLTestProgram, self).runTests()
+ finally:
+ if output_file is not None:
+ output_file.close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/unittest-xml-reporting-2.2.1/xmlrunner/version.py
new/unittest-xml-reporting-2.5.1/xmlrunner/version.py
--- old/unittest-xml-reporting-2.2.1/xmlrunner/version.py 2019-01-09
07:52:55.000000000 +0100
+++ new/unittest-xml-reporting-2.5.1/xmlrunner/version.py 2019-03-25
06:05:10.000000000 +0100
@@ -1,2 +1,2 @@

-__version__ = '2.2.1'
+__version__ = '2.5.1'


< Previous Next >
This Thread
  • No further messages