Hello community, here is the log from the commit of package python-pytest-spec for openSUSE:Factory checked in at 2019-12-18 14:47:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest-spec (Old) and /work/SRC/openSUSE:Factory/.python-pytest-spec.new.4691 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-spec" Wed Dec 18 14:47:07 2019 rev:3 rq:757721 version:2.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest-spec/python-pytest-spec.changes 2019-08-22 15:17:57.590341467 +0200 +++ /work/SRC/openSUSE:Factory/.python-pytest-spec.new.4691/python-pytest-spec.changes 2019-12-18 14:48:52.733953999 +0100 @@ -1,0 +2,10 @@ +Wed Dec 18 04:32:03 UTC 2019 - John Vandenberg <[email protected]> + +- Dropped no longer necessary pytest4.patch +- Update to v2.0.0 + * Update documentation + * New format of output + * Fix small warnings + * Generate the package in wheel format + +------------------------------------------------------------------- Old: ---- pytest-spec-1.1.0.tar.gz pytest4.patch New: ---- pytest-spec-2.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest-spec.spec ++++++ --- /var/tmp/diff_new_pack.W600rG/_old 2019-12-18 14:48:54.509954812 +0100 +++ /var/tmp/diff_new_pack.W600rG/_new 2019-12-18 14:48:54.533954822 +0100 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pytest-spec -Version: 1.1.0 +Version: 2.0.0 Release: 0 Summary: Plugin to display pytest execution output like a specification License: GPL-2.0-only @@ -26,7 +26,6 @@ URL: https://github.com/pchomik/pytest-spec Source: https://files.pythonhosted.org/packages/source/p/pytest-spec/pytest-spec-%{version}.tar.gz Source1: https://raw.githubusercontent.com/pchomik/pytest-spec/master/LICENSE.txt -Patch0: pytest4.patch BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -43,7 +42,6 @@ %prep %setup -q -n pytest-spec-%{version} -%patch0 -p1 cp %{SOURCE1} . %build ++++++ pytest-spec-1.1.0.tar.gz -> pytest-spec-2.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/PKG-INFO new/pytest-spec-2.0.0/PKG-INFO --- old/pytest-spec-1.1.0/PKG-INFO 2016-12-06 22:47:11.000000000 +0100 +++ new/pytest-spec-2.0.0/PKG-INFO 2019-12-17 22:10:35.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: pytest-spec -Version: 1.1.0 +Version: 2.0.0 Summary: pytest plugin to display test execution output like a SPECIFICATION Home-page: https://github.com/pchomik/pytest-spec Author: Pawel Chomicki diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/README.rst new/pytest-spec-2.0.0/README.rst --- old/pytest-spec-1.1.0/README.rst 2016-12-06 21:10:46.000000000 +0100 +++ new/pytest-spec-2.0.0/README.rst 2019-12-17 22:09:46.000000000 +0100 @@ -9,6 +9,8 @@ * Group tests by classes and files * Failed, passed and skipped are marked and colored. * Remove test\_ and underscores for every test. +* Supports function based, class based test. +* Supports describe like tests. Output example @@ -18,15 +20,17 @@ py.test --spec - test/test_results/test_as_class.py::TestResults - [SKIP] Some method return none - [FAIL] Some method returns false - [PASS] Some method returns true - - test/test_results/test_as_functions.py - [PASS] Some method returns true - [FAIL] Some method returns false - [SKIP] Some method return none + test/test_results/test_as_class.py: + + Results: + ✗ Some method return none + ? Some method returns false + ✓ Some method returns true + + test/test_results/test_as_functions.py: + ✗ Some method returns false + ? Some method return none + ✓ Some method returns true Configuration @@ -57,12 +61,12 @@ Continuous Integration ====================== -.. image:: https://drone.io/github.com/pchomik/pytest-spec/status.png - :target: https://drone.io/github.com/pchomik/pytest-spec/latest +.. image:: https://api.travis-ci.org/pchomik/pytest-spec.svg?branch=master + :target: https://travis-ci.org/pchomik/pytest-spec Download ======== -Latest version of plugin is available in `drone.io project artifacts <https://drone.io/github.com/pchomik/pytest-spec/files>`_. +All versions of library are available on official `pypi server <https://pypi.org/project/pytest-spec/#history>`_. Install ======= @@ -76,8 +80,12 @@ Contributors ============ -* dtk -* Eric Carmichael +* @0x64746b +* @lucasmarshall +* @amcgregor +* @jhermann +* @frenzymadness +* @chrischambers Future plans ============ @@ -88,7 +96,7 @@ ======= pytest-spec - pytest plugin to display test execution output like a SPECIFICATION. -Copyright (C) 2014-2016 Pawel Chomicki +Copyright (C) 2014-2019 Pawel Chomicki This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/pytest_spec/patch.py new/pytest-spec-2.0.0/pytest_spec/patch.py --- old/pytest-spec-1.1.0/pytest_spec/patch.py 2016-12-06 21:02:09.000000000 +0100 +++ new/pytest-spec-2.0.0/pytest_spec/patch.py 2019-12-17 21:45:06.000000000 +0100 @@ -11,16 +11,51 @@ def pytest_runtest_logstart(self, nodeid, location): """Signal the start of running a single test item. - Hook has to be disabled because additional information may break output formatting. + Hook has to be disabled because additional information may break output + formatting. """ + pass + + +def pytest_collection_modifyitems(session, config, items): + def depth(f): + return len(f.listchain()) - 1 + + def get_module_name(f): + return f.listchain()[1].name + items.sort(key=depth) + items.sort(key=get_module_name) + return items + + +def get_report_scopes(report): + """ + Returns a list of the report's nested scopes, excluding the module. + + >>> report = lambda s: s + >>> report.nodeid = ( + "specs/user.py::describe_a_user::" + "describe_email_address::cannot_be_hotmail" + ) + >>> get_report_scopes(report) + ['describe_a_user', 'describe_email_address'] + """ + return [i for i in report.nodeid.split('::')[1:-1] if i != '()'] def pytest_runtest_logreport(self, report): - """Process a test setup/call/teardown report relating to the respective phase of executing a test. + """ + Process a test setup/call/teardown report relating to the respective phase + of executing a test. - Hook changed to define SPECIFICATION like output format. This hook will overwrite also VERBOSE option. + Hook changed to define SPECIFICATION like output format. This hook will + overwrite also VERBOSE option. """ - res = self.config.hook.pytest_report_teststatus(report=report) + self.previous_scopes = getattr(self, 'previous_scopes', []) + self.current_scopes = get_report_scopes(report) + indent = self.config.getini('spec_indent') + + res = self.config.hook.pytest_report_teststatus(report=report, config=self.config) cat, letter, word = res self.stats.setdefault(cat, []).append(report) if not letter and not word: @@ -30,11 +65,21 @@ test_path = _get_test_path(report.nodeid, self.config.getini('spec_header_format')) if test_path != self.currentfspath: self.currentfspath = test_path - _print_class_information(self) + _print_description(self) + + if self.previous_scopes != self.current_scopes: + msg = [i for i in self.current_scopes if i not in self.previous_scopes] + msg = [indent * ind + prettify_description(item) + for ind, item in enumerate(msg, len(self.current_scopes) - 1)] + msg = "\n".join(msg) + if msg: + _print_description(self, msg) + self.previous_scopes = self.current_scopes if not isinstance(word, tuple): test_name = _get_test_name(report.nodeid) - markup, test_status = _format_results(report) - _print_test_result(self, test_name, test_status, markup) + markup, test_status = _format_results(report, self.config) + depth = len(self.current_scopes) + _print_test_result(self, test_name, test_status, markup, depth) def _is_nodeid_has_test(nodeid): @@ -43,34 +88,55 @@ return False +def prettify(string): + return _capitalize_first_letter( + _replace_underscores( + _remove_test_container_prefix( + _remove_file_extension(string)))) + + +def prettify_test(string): + return prettify(_remove_test_prefix(string)) + + +def prettify_description(string): + return prettify(_append_colon(_remove_test_container_prefix(string))) + + def _get_test_path(nodeid, header): levels = nodeid.split("::") + module_path = levels[0] + module_name = os.path.split(levels[0])[1] + if len(levels) > 2: class_name = levels[1] - test_case = _split_words(_remove_class_prefix(class_name)) + test_case = prettify(class_name) else: - module_name = os.path.split(levels[0])[1] class_name = '' - test_case = _capitalize_first_letter(_replace_underscores(_remove_test_prefix(_remove_file_extension(module_name)))) + test_case = prettify(module_name) - return header.format(path=levels[0], class_name=class_name, test_case=test_case) + return header.format( + path=levels[0], + module_name=module_name, + module_path=module_path, + class_name=class_name, + test_case=test_case + ) -def _print_class_information(self): +def _print_description(self, msg=None): + if msg is None: + msg = self.currentfspath if hasattr(self, '_first_triggered'): self._tw.line() self._tw.line() - self._tw.write(self.currentfspath) + self._tw.write(msg) self._first_triggered = True -def _remove_class_prefix(nodeid): - return re.sub("^Test", "", nodeid) - - -def _split_words(nodeid): - return re.sub(r"([A-Z])", r" \1", nodeid).strip() +def _remove_test_container_prefix(nodeid): + return re.sub("^(Test)|(describe)", "", nodeid) def _remove_file_extension(nodeid): @@ -93,8 +159,12 @@ return s[:1].capitalize() + s[1:] +def _append_colon(string): + return "{}:".format(string) + + def _get_test_name(nodeid): - test_name = _capitalize_first_letter(_replace_underscores(_remove_test_prefix(_remove_module_name(nodeid)))) + test_name = prettify_test(_remove_module_name(nodeid)) if test_name[:1] is ' ': test_name_parts = test_name.split(' ') if len(test_name_parts) == 1: @@ -103,15 +173,23 @@ return test_name -def _format_results(report): +def _format_results(report, config): + success_indicator = config.getini('spec_success_indicator') + failure_indicator = config.getini('spec_failure_indicator') + skipped_indicator = config.getini('spec_skipped_indicator') if report.passed: - return {'green': True}, 'PASS' + return {'green': True}, success_indicator elif report.failed: - return {'red': True}, 'FAIL' + return {'red': True}, failure_indicator elif report.skipped: - return {'yellow': True}, 'SKIP' + return {'yellow': True}, skipped_indicator -def _print_test_result(self, test_name, test_status, markup): +def _print_test_result(self, test_name, test_status, markup, depth): + indent = self.config.getini('spec_indent') self._tw.line() - self._tw.write(" "+self.config.getini('spec_test_format').format(result=test_status, name=test_name), **markup) + self._tw.write( + indent * depth + self.config.getini('spec_test_format').format( + result=test_status, name=test_name + ), **markup + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/pytest_spec/plugin.py new/pytest-spec-2.0.0/pytest_spec/plugin.py --- old/pytest-spec-1.1.0/pytest_spec/plugin.py 2016-12-06 21:02:37.000000000 +0100 +++ new/pytest-spec-2.0.0/pytest_spec/plugin.py 2019-12-17 20:42:00.000000000 +0100 @@ -3,7 +3,7 @@ :author: Pawel Chomicki """ -from .replacer import logstart_replacer, report_replacer +from .replacer import logstart_replacer, report_replacer, modifyitems_replacer def pytest_addoption(parser): @@ -18,14 +18,34 @@ # register config options parser.addini( 'spec_header_format', - default='{path}::{class_name}', + default='{module_path}:', help='The format of the test headers when using the spec plugin' ) parser.addini( 'spec_test_format', - default='[{result}] {name}', + default='{result} {name}', help='The format of the test results when using the spec plugin' ) + parser.addini( + 'spec_success_indicator', + default='✓', + help='The indicator displayed when a test passes' + ) + parser.addini( + 'spec_failure_indicator', + default='✗', + help='The indicator displayed when a test fails' + ) + parser.addini( + 'spec_skipped_indicator', + default='?', + help='The indicator displayed when a test is skipped' + ) + parser.addini( + 'spec_indent', + default=' ', + help='The string used for indentation in the spec output' + ) def pytest_configure(config): @@ -34,4 +54,5 @@ import _pytest _pytest.terminal.TerminalReporter.pytest_runtest_logstart = logstart_replacer _pytest.terminal.TerminalReporter.pytest_runtest_logreport = report_replacer + _pytest.terminal.TerminalReporter.pytest_collection_modifyitems = modifyitems_replacer imp.reload(_pytest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/pytest_spec/replacer.py new/pytest-spec-2.0.0/pytest_spec/replacer.py --- old/pytest-spec-1.1.0/pytest_spec/replacer.py 2016-12-01 21:58:19.000000000 +0100 +++ new/pytest-spec-2.0.0/pytest_spec/replacer.py 2019-12-17 20:42:00.000000000 +0100 @@ -1,11 +1,16 @@ # -*- coding: utf-8 -*- """Module contains method for replace operation. -Additional method are necessary because self is not yet defined and module doesn't have access to it. +Additional method are necessary because self is not yet defined and module +doesn't have access to it. :author: Pawel Chomicki """ -from .patch import pytest_runtest_logstart, pytest_runtest_logreport +from .patch import ( + pytest_runtest_logstart, + pytest_runtest_logreport, + pytest_collection_modifyitems +) def logstart_replacer(self, nodeid, location): @@ -18,3 +23,9 @@ def wrapper(): return pytest_runtest_logreport(self, report) return wrapper() + + +def modifyitems_replacer(session, config, items): + def wrapper(): + return pytest_collection_modifyitems(session, config, items) + return wrapper() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/pytest_spec.egg-info/PKG-INFO new/pytest-spec-2.0.0/pytest_spec.egg-info/PKG-INFO --- old/pytest-spec-1.1.0/pytest_spec.egg-info/PKG-INFO 2016-12-06 22:47:10.000000000 +0100 +++ new/pytest-spec-2.0.0/pytest_spec.egg-info/PKG-INFO 2019-12-17 22:10:35.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: pytest-spec -Version: 1.1.0 +Version: 2.0.0 Summary: pytest plugin to display test execution output like a SPECIFICATION Home-page: https://github.com/pchomik/pytest-spec Author: Pawel Chomicki diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/setup.cfg new/pytest-spec-2.0.0/setup.cfg --- old/pytest-spec-1.1.0/setup.cfg 2016-12-06 22:47:11.000000000 +0100 +++ new/pytest-spec-2.0.0/setup.cfg 2019-12-17 22:10:35.000000000 +0100 @@ -1,8 +1,7 @@ -[pytest] +[tool:pytest] pep8maxlinelength = 150 [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/setup.py new/pytest-spec-2.0.0/setup.py --- old/pytest-spec-1.1.0/setup.py 2016-12-06 21:05:33.000000000 +0100 +++ new/pytest-spec-2.0.0/setup.py 2019-12-17 21:46:33.000000000 +0100 @@ -13,7 +13,7 @@ setup( name="pytest-spec", packages=['pytest_spec'], - version="1.1.0", + version="2.0.0", entry_points={'pytest11': ['pytest_spec = pytest_spec.plugin']}, description="pytest plugin to display test execution output like a SPECIFICATION", author="Pawel Chomicki", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-spec-1.1.0/test/test_patch.py new/pytest-spec-2.0.0/test/test_patch.py --- old/pytest-spec-1.1.0/test/test_patch.py 2016-12-06 21:05:28.000000000 +0100 +++ new/pytest-spec-2.0.0/test/test_patch.py 2019-12-17 21:45:53.000000000 +0100 @@ -14,7 +14,7 @@ self.letter = kwargs.get('letter', ' ') self.word = kwargs.get('word', ' ') - def pytest_report_teststatus(self, report): + def pytest_report_teststatus(self, report, config): return self.cat, self.letter, self.word @@ -23,12 +23,20 @@ self.hook = FakeHook(*args, **kwargs) def getini(self, option): - if option == 'spec_header_format': - return '{path}::{class_name}' - elif option == 'spec_test_format': - return '[{result}] {name}' - else: - raise TypeError('Option {} is not supported in the test'.format(option)) + mapping = { + 'spec_header_format': '{module_path}:', + 'spec_test_format': '{result} {name}', + 'spec_success_indicator': '✓', + 'spec_failure_indicator': '✗', + 'spec_skipped_indicator': '?', + 'spec_indent': ' ', + } + result = mapping.get(option, None) + if not result: + raise TypeError('Option {} is not supported in the test'.format( + option) + ) + return result class FakeStats(object): @@ -71,22 +79,31 @@ def test__pytest_runtest_logreport__prints_class_name_before_first_test_result(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::Test_example_demo')) - fake_self._tw.write.assert_has_calls([call('Test::Second')]) + fake_self._tw.write.assert_has_calls([call('Second:')]) def test__pytest_runtest_logreport__prints_test_name_and_passed_status(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::test_example_demo')) - fake_self._tw.write.assert_has_calls([call(' [PASS] Example demo', green=True)]) + fake_self._tw.write.assert_has_calls([ + call('Second:'), + call(' ✓ Example demo', green=True) + ]) def test__pytest_runtest_logreport__prints_test_name_and_failed_status(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::test_example_demo', passed=False, failed=True)) - fake_self._tw.write.assert_has_calls([call(' [FAIL] Example demo', red=True)]) + fake_self._tw.write.assert_has_calls([ + call('Second:'), + call(' ✗ Example demo', red=True) + ]) def test__pytest_runtest_logreport__prints_test_name_and_skipped_status(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::test_example_demo', passed=False, skipped=True)) - fake_self._tw.write.assert_has_calls([call(' [SKIP] Example demo', yellow=True)]) + fake_self._tw.write.assert_has_calls([ + call('Second:'), + call(' ? Example demo', yellow=True) + ]) def test__pytest_runtest_logreport__skips_empty_line_for_first_test(self): fake_self = FakeSelf() @@ -97,17 +114,26 @@ def test__pytest_runtest_logreport__marks_method_marked_by_double_underscores(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::test__example__demo')) - fake_self._tw.write.assert_has_calls([call(' [PASS] Example demo', green=True)]) + fake_self._tw.write.assert_has_calls([ + call('Second:'), + call(' ✓ Example demo', green=True) + ]) def test__pytest_runtest_logreport__prints_test_name_and_handle_only_single_marker(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::test__example')) - fake_self._tw.write.assert_has_calls([call(' [PASS] Example', green=True)]) + fake_self._tw.write.assert_has_calls([ + call('Second:'), + call(' ✓ Example', green=True) + ]) def test__pytest_runtest_logreport__honors_capitalization_of_words_in_test_name(self): fake_self = FakeSelf() pytest_runtest_logreport(fake_self, FakeReport('Test::Second::test_example_Demo_CamelCase')) - fake_self._tw.write.assert_has_calls([call(' [PASS] Example Demo CamelCase', green=True)]) + fake_self._tw.write.assert_has_calls([ + call('Second:'), + call(' ✓ Example Demo CamelCase', green=True) + ]) if __name__ == '__main__':
