From patchwork Wed Jul 12 05:30:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: auh@moto-timo.dev X-Patchwork-Id: 27529 Return-Path: Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id DA7C6C001DC for ; Wed, 12 Jul 2023 05:30:10 +0000 (UTC) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by mx.groups.io with SMTP id smtpd.web10.931.1689139803685746643 for ; Tue, 11 Jul 2023 22:30:05 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@moto-timo.dev header.s=gm1 header.b=FbuFQAtw; spf=pass (domain: moto-timo.dev, ip: 217.70.183.201, mailfrom: auh@moto-timo.dev) Received: by mail.gandi.net (Postfix) with ESMTPSA id EB6731BF205 for ; Wed, 12 Jul 2023 05:29:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=moto-timo.dev; s=gm1; t=1689139801; h=from:from:reply-to:subject:subject:to:to:cc:mime-version:mime-version: content-type:content-type; bh=0/oSdWwjWKRJLTtMATugpZy4x/vf67pi4F6BRmNfm6k=; b=FbuFQAtwrkqRwGssAL3D9X2EI/TQXk8iR8Elbawhn24L0EV56GxmIaGbH75+Fr7sCMcbux uyDXbNcoHN5Kkj2mv45yfulbJrfwlnXRVPgTqm9+8G3rk6tiCG0vX5k7+2di0Qoj7iGmE1 O+Zw3YWhNP3nj4L8BsERJ6u0HCBoIGQ0hQaID6dropyBLlXc35MWokuLZ/hW50x+ksZ/QA Zm1iNKMqyXWcyKbRBsWF+N610eFSaO3WhHpIEmf92BKcLIkeFIXRPCE6ysqAPd9joJbxzJ CD22twPetFE5Oh/CO8vXF6jzjKsVBR7hffVFrQz7GzUizeOkUa8dzuqzw+a6Tg== MIME-Version: 1.0 From: auh@moto-timo.dev To: openembedded-devel@lists.openembedded.org Subject: [AUH] python3-behave: upgrading to 6 FAILED X-GND-Sasl: auh@moto-timo.dev List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Wed, 12 Jul 2023 05:30:10 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/103748 Message-Id: <20230712053010.DA7C6C001DC@smtp.lore.kernel.org> Date: Wed, 12 Jul 2023 05:30:10 +0000 (UTC) Hello, this email is a notification from the Auto Upgrade Helper that the automatic attempt to upgrade the recipe *python3-behave* to *6* has Failed(do_compile). Detailed error information: do_compile failed Next steps: - apply the patch: git am 0001-python3-behave-upgrade-1.2.6-git9520119376046aeff738.patch - check the changes to upstream patches and summarize them in the commit message, - compile an image that contains the package - perform some basic sanity tests - amend the patch and sign it off: git commit -s --reset-author --amend - send it to the appropriate mailing list Alternatively, if you believe the recipe should not be upgraded at this time, you can fill RECIPE_NO_UPDATE_REASON in respective recipe file so that automatic upgrades would no longer be attempted. Please review the attached files for further information and build/update failures. Any problem please file a bug at https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Automated%20Update%20Handler Regards, The Upgrade Helper -- >8 -- From 815d53cd9a095b0b67dc6f98ad9a7fa251edefbf Mon Sep 17 00:00:00 2001 From: Upgrade Helper Date: Tue, 11 Jul 2023 17:12:25 -0500 Subject: [PATCH] python3-behave: upgrade 1.2.6+git9520119376046aeff73804b5f1ea05d87a63f370 -> 6 --- ...ioOutlineBuilder-was-not-copying-des.patch | 22 + .../0002-UPDATE-FIXED-725.patch | 21 + ...ST-to-verify-that-issue-725-is-fixed.patch | 60 + ...ter-counts-computation-when-Rules-ar.patch | 342 + ...print_summary-Simplify-if-Rules-are-.patch | 60 + ...r-Add-basic-support-output-for-Rules.patch | 395 + ...MP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch | 67 + .../0008-Correct-examples-and-docstring.patch | 41 + ...ure.run_items-processing-with-Rule-s.patch | 58 + ...-sphinx-intl-support-for-READTHEDOCS.patch | 58 + ...ent-package-versions-in-requirements.patch | 81 + .../0012-docs-conf.py-tweaks.patch | 30 + ...lled-after_rule-hook-was-after_after.patch | 30 + ...d-hints-on-Gherkin-v6-grammar-issues.patch | 45 + .../0015-README-ReST-tweaks.patch | 18 + ...016-Example-using-Gherkin-v6-grammar.patch | 228 + .../0017-PREPARE-Python-3.8-support.patch | 39 + ...x-logging.Formatter-validate-problem.patch | 22 + ...arily-move-py38-dev-to-front-build-f.patch | 28 + ...Tweaks-for-faster-builds-temporarily.patch | 35 + ...8-logging.Formatter.validate-problem.patch | 47 + ...on-3.8-asyncio.coroutine-is-deprecat.patch | 42 + .../0023-UPDATE-Add-755-info.patch | 24 + ...ted-to-docstring-example-and-weird-b.patch | 25 + ...pe-sequence-warnings-w-regex-pattern.patch | 29 + ...NUP-Move-deprecated-tag-matcher-clas.patch | 1020 +++ .../python3-behave/0027-Comment-tweaks.patch | 30 + ...-related-to-invalid-escapes-in-regex.patch | 79 + ...ould-not-break-configured-rerun-sett.patch | 73 + ...les-and-failing-scenarios-enable-via.patch | 36 + ..._v6-Tweak-ScenarioOutline-Examples-t.patch | 27 + .../0032-Add-info-on-merged-pull-588.patch | 21 + ...3-Tweak-tests-required-by-pytest-5.0.patch | 97 + ...st-instead-of-nose-to-remove-nose.im.patch | 180 + ...Y-nose-to-avoid-nose.importer-warnin.patch | 1815 ++++ ...0036-FIX-Remove-test-from-pytest-run.patch | 22 + ...on-Add-support-for-Scenario-containe.patch | 652 ++ ...tion-for-Select-by-location-for-Scen.patch | 58 + .../0039-tests-Fix-warnings-for-python3.patch | 50 + ...ag-expressions-1.1.2-to-fix-warnings.patch | 55 + ...ENT-Support-emojis-in-feature-files-.patch | 91 + ...valid-escape-char-in-regex-w-python3.patch | 250 + ...3-Example-related-to-question-in-756.patch | 335 + ...X-python3.8-regressions-on-CI-server.patch | 489 + .../0045-UPDATE-Mark-issue-755-as-fixed.patch | 46 + ...DATE-Cucumber-gherkin-languages.json.patch | 57 + ...ule-keyword-translation-in-portugues.patch | 202 + ...-generate-from-gherkin-languages.jso.patch | 141 + ...ming-to-fixture.behave.no_background.patch | 322 + ...50-EXAMPLE-Cleanup-Gherkin-v6-README.patch | 64 + ...for-feature.background-inheritance-f.patch | 1510 +++ ...-Add-support-for-runtime-constraints.patch | 269 + .../0053-Use-runtime-constraints.patch | 196 + ...0054-CLEANUP-Remove-deprecated-parts.patch | 3937 ++++++++ ...0055-CLEANUP-Remove-deprecated-parts.patch | 736 ++ ...rform-more-Gherkin-v6-checks-and-run.patch | 155 + ...-and-python-module-old-was-broken-no.patch | 72 + .../0058-UTIL-Formatting-tweaks.patch | 22 + ...e-use_fixture_by_tag-didn-t-return-t.patch | 23 + .../0060-Added-issue-unit-test.patch | 62 + ...e-pull-request-767-with-minor-tweaks.patch | 60 + ...sue-766-PrettyFormatter-UnicodeError.patch | 83 + ...enarioOutline.Examples-without-table.patch | 74 + ...enarioOutline.Examples-without-table.patch | 21 + .../0065-Nibble-TravisCI-to-wake-up.patch | 21 + .../0066-Tweak-pytest-version-selection.patch | 37 + ...figuration-to-silence-JUnit-XML-dial.patch | 37 + ...e-test-for-wildcard-pattern-matching.patch | 56 + ...ATE-dependencies-path.py-path-pytest.patch | 141 + ...eprecatedWarning-from-distutils-pack.patch | 25 + ...-Add-ContextMode-enum-related-to-797.patch | 216 + .../0072-Cleanup-comments.patch | 22 + ...phinx-build-problem-async_steps3x.py.patch | 29 + ...-parse_expressions-was-parse_builtin.patch | 185 + ...ssion-add-links-to-parse_type-module.patch | 40 + ...MP-VERSION-1.2.7.dev2-was-1.2.7.dev1.patch | 65 + ...leanups-related-to-question-in-800-P.patch | 223 + ...y-name-uses-regex-pattern-related-to.patch | 82 + ...nce-problem-copy-paste-in-Rule-class.patch | 34 + ...API-description-for-Runner-Operation.patch | 195 + ...0081-FIX-DOCS-Runner-operations-typo.patch | 22 + ...ement-Context.add_cleanup-with-layer.patch | 295 + ...8.0-parse-versions-1.16.0-.-1.17.x-h.patch | 37 + ...Duplicated-steps-AmbiguousStepErrors.patch | 34 + .../0085-Add-renovate.json.patch | 21 + ...086-PRPEPARE-RENOVATE-With-adaptions.patch | 175 + .../0087-Pin-dependencies.patch | 36 + ...te-Extend-pip-requirements-file-list.patch | 31 + ...IN-REQUIREMENTS-Extend-to-all-places.patch | 92 + ..._cleanup-replaces-_tasklet_cleanup-r.patch | 8116 +++++++++++++++++ ...nge-code-blocks-from-bash-to-console.patch | 36 + .../0092-Fix-typo-in-tutorial.patch | 24 + ...nts-Use-PyHamcrest-2.0-for-python2.7.patch | 80 + .../0094-UPDATE-PR-877-was-merged.patch | 21 + .../0095-capitalizing-steps.patch | 28 + ...develop.update-gherkin-that-aborted-.patch | 56 + ...ainst-PowerPC-CPU-support-Travis-867.patch | 22 + ...098-Add-Context.attach-docs-and-test.patch | 132 + .../0099-Add-Contributing-chapter.patch | 125 + ...apt-Tox-target-for-building-the-docs.patch | 34 + ...tion-HTML-formatter-in-documentation.patch | 83 + ...le-highlighting-for-pip-install-docs.patch | 22 + ...ocs-fix-simple-typo-tuorial-tutorial.patch | 52 + ...t-for-python3.9-by-using-active-tags.patch | 227 + .../0105-PREFER-python3-from-now-on.patch | 19 + .../0106-REMOVE-invoke-scripts.patch | 41 + ...X-Deprecated-warnings-for-Python-3.x.patch | 124 + ...lems-in-virtualenvs-w-behave4cmd0-st.patch | 18 + .../0109-FIX-Active-tag-logic.patch | 875 ++ .../0110-FIX-Tests-w-more.features.patch | 56 + ...FIX-Some-regressions-with-Python-3.9.patch | 741 ++ .../0112-docs-Update-new-and-noteworthy.patch | 84 + ...elper-function-to-print-the-current-.patch | 278 + ...kin-languages.json-from-cucumber-rep.patch | 541 ++ ...TE-CHANGES-Related-to-PR-895-and-827.patch | 22 + ...r-python-2.7-builds-mock-4.0-only-fo.patch | 39 + ...-to-use-a-custom-runner-in-the-behav.patch | 126 + ...llow-forcing-color-with-color-always.patch | 59 + ...lor-with-no-value-followed-by-posarg.patch | 43 + .../0120-Add-BEHAVE_COLOR-env-var.patch | 31 + ...121-fix-malformed-table-rows-warning.patch | 33 + ...-955-setup-Remove-attribute-use_2to3.patch | 42 + .../0123-Add-info-for-fixed-issue-955.patch | 21 + .../python/python3-behave_1.2.6.bb | 129 +- 124 files changed, 29808 insertions(+), 2 deletions(-) create mode 100644 meta-python/recipes-devtools/python/python3-behave/0001-FIXES-725-ScenarioOutlineBuilder-was-not-copying-des.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0002-UPDATE-FIXED-725.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0003-ADD-TEST-to-verify-that-issue-725-is-fixed.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0004-FIX-SummaryReporter-counts-computation-when-Rules-ar.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0005-SummaryReporter.print_summary-Simplify-if-Rules-are-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0006-Formatter-Add-basic-support-output-for-Rules.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0007-BUMP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0008-Correct-examples-and-docstring.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0009-FIX-feature.run_items-processing-with-Rule-s.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0010-docs-Disable-sphinx-intl-support-for-READTHEDOCS.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0011-Cleanup-Dependent-package-versions-in-requirements.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0012-docs-conf.py-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0013-FIX-Misspelled-after_rule-hook-was-after_after.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0014-Add-hints-on-Gherkin-v6-grammar-issues.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0015-README-ReST-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0016-Example-using-Gherkin-v6-grammar.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0017-PREPARE-Python-3.8-support.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0018-py3.8-Try-to-fix-logging.Formatter-validate-problem.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0019-travis.ci-Temporarily-move-py38-dev-to-front-build-f.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0020-travis.ci-Tweaks-for-faster-builds-temporarily.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0021-FIX-py3.8-logging.Formatter.validate-problem.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0022-PREPARE-FOR-Python-3.8-asyncio.coroutine-is-deprecat.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0023-UPDATE-Add-755-info.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0024-FIX-WARNING-Related-to-docstring-example-and-weird-b.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0025-FIX-invalid-escape-sequence-warnings-w-regex-pattern.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0026-DEPRECATING-CLEANUP-Move-deprecated-tag-matcher-clas.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0027-Comment-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0028-FIX-warnings-related-to-invalid-escapes-in-regex.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0029-Steps-catalog-should-not-break-configured-rerun-sett.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0030-Add-feature-w-rules-and-failing-scenarios-enable-via.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0031-examples-gherkin_v6-Tweak-ScenarioOutline-Examples-t.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0032-Add-info-on-merged-pull-588.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0033-Tweak-tests-required-by-pytest-5.0.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0034-CLEANUP-Use-pytest-instead-of-nose-to-remove-nose.im.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0035-REMOVE-DEPENDENCY-nose-to-avoid-nose.importer-warnin.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0036-FIX-Remove-test-from-pytest-run.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0037-Select-by-location-Add-support-for-Scenario-containe.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0038-docs-Add-description-for-Select-by-location-for-Scen.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0039-tests-Fix-warnings-for-python3.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0040-Use-cucumber-tag-expressions-1.1.2-to-fix-warnings.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0041-MENTION-ENHANCEMENT-Support-emojis-in-feature-files-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0042-FIX-Invalid-escape-char-in-regex-w-python3.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0043-Example-related-to-question-in-756.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0044-FIX-python3.8-regressions-on-CI-server.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0045-UPDATE-Mark-issue-755-as-fixed.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0046-UPDATE-Cucumber-gherkin-languages.json.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0047-gherkin-Adding-Rule-keyword-translation-in-portugues.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0048-Tweaks-to-update-generate-from-gherkin-languages.jso.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0049-EXAMPLE-Tweak-naming-to-fixture.behave.no_background.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0050-EXAMPLE-Cleanup-Gherkin-v6-README.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0051-Improve-support-for-feature.background-inheritance-f.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0052-Add-support-for-runtime-constraints.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0053-Use-runtime-constraints.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0054-CLEANUP-Remove-deprecated-parts.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0055-CLEANUP-Remove-deprecated-parts.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0056-more.features-Perform-more-Gherkin-v6-checks-and-run.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0057-UTIL-Correct-URL-and-python-module-old-was-broken-no.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0058-UTIL-Formatting-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0059-Fixed-a-bug-where-use_fixture_by_tag-didn-t-return-t.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0060-Added-issue-unit-test.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0061-Merge-pull-request-767-with-minor-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0062-CHECK-Issue-766-PrettyFormatter-UnicodeError.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0063-FIX-issue-772-ScenarioOutline.Examples-without-table.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0064-FIX-issue-772-ScenarioOutline.Examples-without-table.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0065-Nibble-TravisCI-to-wake-up.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0066-Tweak-pytest-version-selection.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0067-Tweak-pytest-configuration-to-silence-JUnit-XML-dial.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0068-Add-basic-feature-test-for-wildcard-pattern-matching.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0069-UPDATE-dependencies-path.py-path-pytest.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0070-pytest-Disable-DeprecatedWarning-from-distutils-pack.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0071-CLEANUP-Add-ContextMode-enum-related-to-797.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0072-Cleanup-comments.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0073-FIX-sphinx-build-problem-async_steps3x.py.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0074-docs-Rename-page-parse_expressions-was-parse_builtin.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0075-docs-parse_expression-add-links-to-parse_type-module.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0076-BUMP-VERSION-1.2.7.dev2-was-1.2.7.dev1.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0077-Gherkin-parser-Cleanups-related-to-question-in-800-P.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0078-Clarify-select-by-name-uses-regex-pattern-related-to.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0079-FIX-Cross-reference-problem-copy-paste-in-Rule-class.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0080-DOCS-Update-API-description-for-Runner-Operation.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0081-FIX-DOCS-Runner-operations-typo.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0082-issue-740-Enhancement-Context.add_cleanup-with-layer.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0083-UPDATE-parse-1.18.0-parse-versions-1.16.0-.-1.17.x-h.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0084-RELATED-TO-Duplicated-steps-AmbiguousStepErrors.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0085-Add-renovate.json.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0086-PRPEPARE-RENOVATE-With-adaptions.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0087-Pin-dependencies.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0088-renovate-Extend-pip-requirements-file-list.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0089-PIN-REQUIREMENTS-Extend-to-all-places.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0090-tasks-Add-invoke_cleanup-replaces-_tasklet_cleanup-r.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0091-Docs-change-code-blocks-from-bash-to-console.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0092-Fix-typo-in-tutorial.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0093-py.requirements-Use-PyHamcrest-2.0-for-python2.7.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0094-UPDATE-PR-877-was-merged.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0095-capitalizing-steps.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0096-FIX-invoke-task-develop.update-gherkin-that-aborted-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0097-Test-against-PowerPC-CPU-support-Travis-867.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0098-Add-Context.attach-docs-and-test.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0099-Add-Contributing-chapter.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0100-Adapt-Tox-target-for-building-the-docs.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0101-Mention-HTML-formatter-in-documentation.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0102-Use-console-highlighting-for-pip-install-docs.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0103-docs-fix-simple-typo-tuorial-tutorial.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0104-Add-support-for-python3.9-by-using-active-tags.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0105-PREFER-python3-from-now-on.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0106-REMOVE-invoke-scripts.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0107-FIX-Deprecated-warnings-for-Python-3.x.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0108-FIX-Python2-problems-in-virtualenvs-w-behave4cmd0-st.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0109-FIX-Active-tag-logic.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0110-FIX-Tests-w-more.features.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0111-FIX-Some-regressions-with-Python-3.9.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0112-docs-Update-new-and-noteworthy.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0113-Add-diagnostic-helper-function-to-print-the-current-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0114-UPDATE-i18n-gherkin-languages.json-from-cucumber-rep.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0115-UPDATE-CHANGES-Related-to-PR-895-and-827.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0116-FIX-CI-TRAVIS-For-python-2.7-builds-mock-4.0-only-fo.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0117-Adds-the-ability-to-use-a-custom-runner-in-the-behav.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0118-Allow-forcing-color-with-color-always.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0119-Allow-color-with-no-value-followed-by-posarg.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0120-Add-BEHAVE_COLOR-env-var.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0121-fix-malformed-table-rows-warning.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0122-FIX-955-setup-Remove-attribute-use_2to3.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0123-Add-info-for-fixed-issue-955.patch From 815d53cd9a095b0b67dc6f98ad9a7fa251edefbf Mon Sep 17 00:00:00 2001 From: Upgrade Helper Date: Tue, 11 Jul 2023 17:12:25 -0500 Subject: [PATCH] python3-behave: upgrade 1.2.6+git9520119376046aeff73804b5f1ea05d87a63f370 -> 6 --- ...ioOutlineBuilder-was-not-copying-des.patch | 22 + .../0002-UPDATE-FIXED-725.patch | 21 + ...ST-to-verify-that-issue-725-is-fixed.patch | 60 + ...ter-counts-computation-when-Rules-ar.patch | 342 + ...print_summary-Simplify-if-Rules-are-.patch | 60 + ...r-Add-basic-support-output-for-Rules.patch | 395 + ...MP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch | 67 + .../0008-Correct-examples-and-docstring.patch | 41 + ...ure.run_items-processing-with-Rule-s.patch | 58 + ...-sphinx-intl-support-for-READTHEDOCS.patch | 58 + ...ent-package-versions-in-requirements.patch | 81 + .../0012-docs-conf.py-tweaks.patch | 30 + ...lled-after_rule-hook-was-after_after.patch | 30 + ...d-hints-on-Gherkin-v6-grammar-issues.patch | 45 + .../0015-README-ReST-tweaks.patch | 18 + ...016-Example-using-Gherkin-v6-grammar.patch | 228 + .../0017-PREPARE-Python-3.8-support.patch | 39 + ...x-logging.Formatter-validate-problem.patch | 22 + ...arily-move-py38-dev-to-front-build-f.patch | 28 + ...Tweaks-for-faster-builds-temporarily.patch | 35 + ...8-logging.Formatter.validate-problem.patch | 47 + ...on-3.8-asyncio.coroutine-is-deprecat.patch | 42 + .../0023-UPDATE-Add-755-info.patch | 24 + ...ted-to-docstring-example-and-weird-b.patch | 25 + ...pe-sequence-warnings-w-regex-pattern.patch | 29 + ...NUP-Move-deprecated-tag-matcher-clas.patch | 1020 +++ .../python3-behave/0027-Comment-tweaks.patch | 30 + ...-related-to-invalid-escapes-in-regex.patch | 79 + ...ould-not-break-configured-rerun-sett.patch | 73 + ...les-and-failing-scenarios-enable-via.patch | 36 + ..._v6-Tweak-ScenarioOutline-Examples-t.patch | 27 + .../0032-Add-info-on-merged-pull-588.patch | 21 + ...3-Tweak-tests-required-by-pytest-5.0.patch | 97 + ...st-instead-of-nose-to-remove-nose.im.patch | 180 + ...Y-nose-to-avoid-nose.importer-warnin.patch | 1815 ++++ ...0036-FIX-Remove-test-from-pytest-run.patch | 22 + ...on-Add-support-for-Scenario-containe.patch | 652 ++ ...tion-for-Select-by-location-for-Scen.patch | 58 + .../0039-tests-Fix-warnings-for-python3.patch | 50 + ...ag-expressions-1.1.2-to-fix-warnings.patch | 55 + ...ENT-Support-emojis-in-feature-files-.patch | 91 + ...valid-escape-char-in-regex-w-python3.patch | 250 + ...3-Example-related-to-question-in-756.patch | 335 + ...X-python3.8-regressions-on-CI-server.patch | 489 + .../0045-UPDATE-Mark-issue-755-as-fixed.patch | 46 + ...DATE-Cucumber-gherkin-languages.json.patch | 57 + ...ule-keyword-translation-in-portugues.patch | 202 + ...-generate-from-gherkin-languages.jso.patch | 141 + ...ming-to-fixture.behave.no_background.patch | 322 + ...50-EXAMPLE-Cleanup-Gherkin-v6-README.patch | 64 + ...for-feature.background-inheritance-f.patch | 1510 +++ ...-Add-support-for-runtime-constraints.patch | 269 + .../0053-Use-runtime-constraints.patch | 196 + ...0054-CLEANUP-Remove-deprecated-parts.patch | 3937 ++++++++ ...0055-CLEANUP-Remove-deprecated-parts.patch | 736 ++ ...rform-more-Gherkin-v6-checks-and-run.patch | 155 + ...-and-python-module-old-was-broken-no.patch | 72 + .../0058-UTIL-Formatting-tweaks.patch | 22 + ...e-use_fixture_by_tag-didn-t-return-t.patch | 23 + .../0060-Added-issue-unit-test.patch | 62 + ...e-pull-request-767-with-minor-tweaks.patch | 60 + ...sue-766-PrettyFormatter-UnicodeError.patch | 83 + ...enarioOutline.Examples-without-table.patch | 74 + ...enarioOutline.Examples-without-table.patch | 21 + .../0065-Nibble-TravisCI-to-wake-up.patch | 21 + .../0066-Tweak-pytest-version-selection.patch | 37 + ...figuration-to-silence-JUnit-XML-dial.patch | 37 + ...e-test-for-wildcard-pattern-matching.patch | 56 + ...ATE-dependencies-path.py-path-pytest.patch | 141 + ...eprecatedWarning-from-distutils-pack.patch | 25 + ...-Add-ContextMode-enum-related-to-797.patch | 216 + .../0072-Cleanup-comments.patch | 22 + ...phinx-build-problem-async_steps3x.py.patch | 29 + ...-parse_expressions-was-parse_builtin.patch | 185 + ...ssion-add-links-to-parse_type-module.patch | 40 + ...MP-VERSION-1.2.7.dev2-was-1.2.7.dev1.patch | 65 + ...leanups-related-to-question-in-800-P.patch | 223 + ...y-name-uses-regex-pattern-related-to.patch | 82 + ...nce-problem-copy-paste-in-Rule-class.patch | 34 + ...API-description-for-Runner-Operation.patch | 195 + ...0081-FIX-DOCS-Runner-operations-typo.patch | 22 + ...ement-Context.add_cleanup-with-layer.patch | 295 + ...8.0-parse-versions-1.16.0-.-1.17.x-h.patch | 37 + ...Duplicated-steps-AmbiguousStepErrors.patch | 34 + .../0085-Add-renovate.json.patch | 21 + ...086-PRPEPARE-RENOVATE-With-adaptions.patch | 175 + .../0087-Pin-dependencies.patch | 36 + ...te-Extend-pip-requirements-file-list.patch | 31 + ...IN-REQUIREMENTS-Extend-to-all-places.patch | 92 + ..._cleanup-replaces-_tasklet_cleanup-r.patch | 8116 +++++++++++++++++ ...nge-code-blocks-from-bash-to-console.patch | 36 + .../0092-Fix-typo-in-tutorial.patch | 24 + ...nts-Use-PyHamcrest-2.0-for-python2.7.patch | 80 + .../0094-UPDATE-PR-877-was-merged.patch | 21 + .../0095-capitalizing-steps.patch | 28 + ...develop.update-gherkin-that-aborted-.patch | 56 + ...ainst-PowerPC-CPU-support-Travis-867.patch | 22 + ...098-Add-Context.attach-docs-and-test.patch | 132 + .../0099-Add-Contributing-chapter.patch | 125 + ...apt-Tox-target-for-building-the-docs.patch | 34 + ...tion-HTML-formatter-in-documentation.patch | 83 + ...le-highlighting-for-pip-install-docs.patch | 22 + ...ocs-fix-simple-typo-tuorial-tutorial.patch | 52 + ...t-for-python3.9-by-using-active-tags.patch | 227 + .../0105-PREFER-python3-from-now-on.patch | 19 + .../0106-REMOVE-invoke-scripts.patch | 41 + ...X-Deprecated-warnings-for-Python-3.x.patch | 124 + ...lems-in-virtualenvs-w-behave4cmd0-st.patch | 18 + .../0109-FIX-Active-tag-logic.patch | 875 ++ .../0110-FIX-Tests-w-more.features.patch | 56 + ...FIX-Some-regressions-with-Python-3.9.patch | 741 ++ .../0112-docs-Update-new-and-noteworthy.patch | 84 + ...elper-function-to-print-the-current-.patch | 278 + ...kin-languages.json-from-cucumber-rep.patch | 541 ++ ...TE-CHANGES-Related-to-PR-895-and-827.patch | 22 + ...r-python-2.7-builds-mock-4.0-only-fo.patch | 39 + ...-to-use-a-custom-runner-in-the-behav.patch | 126 + ...llow-forcing-color-with-color-always.patch | 59 + ...lor-with-no-value-followed-by-posarg.patch | 43 + .../0120-Add-BEHAVE_COLOR-env-var.patch | 31 + ...121-fix-malformed-table-rows-warning.patch | 33 + ...-955-setup-Remove-attribute-use_2to3.patch | 42 + .../0123-Add-info-for-fixed-issue-955.patch | 21 + .../python/python3-behave_1.2.6.bb | 129 +- 124 files changed, 29808 insertions(+), 2 deletions(-) create mode 100644 meta-python/recipes-devtools/python/python3-behave/0001-FIXES-725-ScenarioOutlineBuilder-was-not-copying-des.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0002-UPDATE-FIXED-725.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0003-ADD-TEST-to-verify-that-issue-725-is-fixed.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0004-FIX-SummaryReporter-counts-computation-when-Rules-ar.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0005-SummaryReporter.print_summary-Simplify-if-Rules-are-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0006-Formatter-Add-basic-support-output-for-Rules.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0007-BUMP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0008-Correct-examples-and-docstring.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0009-FIX-feature.run_items-processing-with-Rule-s.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0010-docs-Disable-sphinx-intl-support-for-READTHEDOCS.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0011-Cleanup-Dependent-package-versions-in-requirements.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0012-docs-conf.py-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0013-FIX-Misspelled-after_rule-hook-was-after_after.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0014-Add-hints-on-Gherkin-v6-grammar-issues.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0015-README-ReST-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0016-Example-using-Gherkin-v6-grammar.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0017-PREPARE-Python-3.8-support.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0018-py3.8-Try-to-fix-logging.Formatter-validate-problem.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0019-travis.ci-Temporarily-move-py38-dev-to-front-build-f.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0020-travis.ci-Tweaks-for-faster-builds-temporarily.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0021-FIX-py3.8-logging.Formatter.validate-problem.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0022-PREPARE-FOR-Python-3.8-asyncio.coroutine-is-deprecat.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0023-UPDATE-Add-755-info.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0024-FIX-WARNING-Related-to-docstring-example-and-weird-b.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0025-FIX-invalid-escape-sequence-warnings-w-regex-pattern.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0026-DEPRECATING-CLEANUP-Move-deprecated-tag-matcher-clas.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0027-Comment-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0028-FIX-warnings-related-to-invalid-escapes-in-regex.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0029-Steps-catalog-should-not-break-configured-rerun-sett.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0030-Add-feature-w-rules-and-failing-scenarios-enable-via.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0031-examples-gherkin_v6-Tweak-ScenarioOutline-Examples-t.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0032-Add-info-on-merged-pull-588.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0033-Tweak-tests-required-by-pytest-5.0.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0034-CLEANUP-Use-pytest-instead-of-nose-to-remove-nose.im.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0035-REMOVE-DEPENDENCY-nose-to-avoid-nose.importer-warnin.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0036-FIX-Remove-test-from-pytest-run.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0037-Select-by-location-Add-support-for-Scenario-containe.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0038-docs-Add-description-for-Select-by-location-for-Scen.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0039-tests-Fix-warnings-for-python3.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0040-Use-cucumber-tag-expressions-1.1.2-to-fix-warnings.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0041-MENTION-ENHANCEMENT-Support-emojis-in-feature-files-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0042-FIX-Invalid-escape-char-in-regex-w-python3.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0043-Example-related-to-question-in-756.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0044-FIX-python3.8-regressions-on-CI-server.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0045-UPDATE-Mark-issue-755-as-fixed.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0046-UPDATE-Cucumber-gherkin-languages.json.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0047-gherkin-Adding-Rule-keyword-translation-in-portugues.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0048-Tweaks-to-update-generate-from-gherkin-languages.jso.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0049-EXAMPLE-Tweak-naming-to-fixture.behave.no_background.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0050-EXAMPLE-Cleanup-Gherkin-v6-README.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0051-Improve-support-for-feature.background-inheritance-f.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0052-Add-support-for-runtime-constraints.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0053-Use-runtime-constraints.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0054-CLEANUP-Remove-deprecated-parts.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0055-CLEANUP-Remove-deprecated-parts.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0056-more.features-Perform-more-Gherkin-v6-checks-and-run.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0057-UTIL-Correct-URL-and-python-module-old-was-broken-no.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0058-UTIL-Formatting-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0059-Fixed-a-bug-where-use_fixture_by_tag-didn-t-return-t.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0060-Added-issue-unit-test.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0061-Merge-pull-request-767-with-minor-tweaks.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0062-CHECK-Issue-766-PrettyFormatter-UnicodeError.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0063-FIX-issue-772-ScenarioOutline.Examples-without-table.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0064-FIX-issue-772-ScenarioOutline.Examples-without-table.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0065-Nibble-TravisCI-to-wake-up.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0066-Tweak-pytest-version-selection.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0067-Tweak-pytest-configuration-to-silence-JUnit-XML-dial.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0068-Add-basic-feature-test-for-wildcard-pattern-matching.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0069-UPDATE-dependencies-path.py-path-pytest.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0070-pytest-Disable-DeprecatedWarning-from-distutils-pack.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0071-CLEANUP-Add-ContextMode-enum-related-to-797.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0072-Cleanup-comments.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0073-FIX-sphinx-build-problem-async_steps3x.py.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0074-docs-Rename-page-parse_expressions-was-parse_builtin.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0075-docs-parse_expression-add-links-to-parse_type-module.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0076-BUMP-VERSION-1.2.7.dev2-was-1.2.7.dev1.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0077-Gherkin-parser-Cleanups-related-to-question-in-800-P.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0078-Clarify-select-by-name-uses-regex-pattern-related-to.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0079-FIX-Cross-reference-problem-copy-paste-in-Rule-class.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0080-DOCS-Update-API-description-for-Runner-Operation.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0081-FIX-DOCS-Runner-operations-typo.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0082-issue-740-Enhancement-Context.add_cleanup-with-layer.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0083-UPDATE-parse-1.18.0-parse-versions-1.16.0-.-1.17.x-h.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0084-RELATED-TO-Duplicated-steps-AmbiguousStepErrors.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0085-Add-renovate.json.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0086-PRPEPARE-RENOVATE-With-adaptions.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0087-Pin-dependencies.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0088-renovate-Extend-pip-requirements-file-list.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0089-PIN-REQUIREMENTS-Extend-to-all-places.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0090-tasks-Add-invoke_cleanup-replaces-_tasklet_cleanup-r.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0091-Docs-change-code-blocks-from-bash-to-console.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0092-Fix-typo-in-tutorial.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0093-py.requirements-Use-PyHamcrest-2.0-for-python2.7.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0094-UPDATE-PR-877-was-merged.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0095-capitalizing-steps.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0096-FIX-invoke-task-develop.update-gherkin-that-aborted-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0097-Test-against-PowerPC-CPU-support-Travis-867.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0098-Add-Context.attach-docs-and-test.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0099-Add-Contributing-chapter.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0100-Adapt-Tox-target-for-building-the-docs.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0101-Mention-HTML-formatter-in-documentation.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0102-Use-console-highlighting-for-pip-install-docs.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0103-docs-fix-simple-typo-tuorial-tutorial.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0104-Add-support-for-python3.9-by-using-active-tags.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0105-PREFER-python3-from-now-on.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0106-REMOVE-invoke-scripts.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0107-FIX-Deprecated-warnings-for-Python-3.x.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0108-FIX-Python2-problems-in-virtualenvs-w-behave4cmd0-st.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0109-FIX-Active-tag-logic.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0110-FIX-Tests-w-more.features.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0111-FIX-Some-regressions-with-Python-3.9.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0112-docs-Update-new-and-noteworthy.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0113-Add-diagnostic-helper-function-to-print-the-current-.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0114-UPDATE-i18n-gherkin-languages.json-from-cucumber-rep.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0115-UPDATE-CHANGES-Related-to-PR-895-and-827.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0116-FIX-CI-TRAVIS-For-python-2.7-builds-mock-4.0-only-fo.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0117-Adds-the-ability-to-use-a-custom-runner-in-the-behav.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0118-Allow-forcing-color-with-color-always.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0119-Allow-color-with-no-value-followed-by-posarg.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0120-Add-BEHAVE_COLOR-env-var.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0121-fix-malformed-table-rows-warning.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0122-FIX-955-setup-Remove-attribute-use_2to3.patch create mode 100644 meta-python/recipes-devtools/python/python3-behave/0123-Add-info-for-fixed-issue-955.patch diff --git a/meta-python/recipes-devtools/python/python3-behave/0001-FIXES-725-ScenarioOutlineBuilder-was-not-copying-des.patch b/meta-python/recipes-devtools/python/python3-behave/0001-FIXES-725-ScenarioOutlineBuilder-was-not-copying-des.patch new file mode 100644 index 000000000..5c49cc00c --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0001-FIXES-725-ScenarioOutlineBuilder-was-not-copying-des.patch @@ -0,0 +1,22 @@ +From b941f353c129f73934853082f3f3a01cebe6f944 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Mon, 11 Mar 2019 22:37:04 +0100 +Subject: [PATCH] FIXES #725: ScenarioOutlineBuilder was not copying + description to created Scenario. + +--- + behave/model.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/behave/model.py b/behave/model.py +index 4ad4b9d..9dd68fd 100644 +--- a/behave/model.py ++++ b/behave/model.py +@@ -1196,6 +1196,7 @@ class ScenarioOutlineBuilder(object): + scenario.feature = scenario_outline.feature + scenario.parent = scenario_outline + scenario.background = scenario_outline.background ++ scenario.description = scenario_outline.description + scenario._row = row # pylint: disable=protected-access + scenarios.append(scenario) + return scenarios diff --git a/meta-python/recipes-devtools/python/python3-behave/0002-UPDATE-FIXED-725.patch b/meta-python/recipes-devtools/python/python3-behave/0002-UPDATE-FIXED-725.patch new file mode 100644 index 000000000..2be6811fb --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0002-UPDATE-FIXED-725.patch @@ -0,0 +1,21 @@ +From 0e26bbae1f9f8d60c3ab9470b3685af1dde5b6d8 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Mon, 11 Mar 2019 22:40:13 +0100 +Subject: [PATCH] UPDATE: FIXED #725 + +--- + CHANGES.rst | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/CHANGES.rst b/CHANGES.rst +index c11840f..d6e96af 100644 +--- a/CHANGES.rst ++++ b/CHANGES.rst +@@ -32,6 +32,7 @@ ENHANCEMENTS: + + FIXED: + ++* issue #725: Scenario Outline description lines seem to be ignored (submitted by: nizwiz) + * issue #713: Background section doesn't support description (provided by: dgou) + * pull #657: Allow async steps with timeouts to fail when they raise exceptions (provided by: ALSchwalm) + * issue #631: ScenarioOutline variables not possible in table headings (provided by: mschnelle, pull #642) diff --git a/meta-python/recipes-devtools/python/python3-behave/0003-ADD-TEST-to-verify-that-issue-725-is-fixed.patch b/meta-python/recipes-devtools/python/python3-behave/0003-ADD-TEST-to-verify-that-issue-725-is-fixed.patch new file mode 100644 index 000000000..3e4f6fdf2 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0003-ADD-TEST-to-verify-that-issue-725-is-fixed.patch @@ -0,0 +1,60 @@ +From 66324f8dc74715a5018d1eced225557c40bd7acd Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Mon, 11 Mar 2019 23:08:00 +0100 +Subject: [PATCH] ADD TEST to verify that issue #725 is fixed. + +--- + tests/issues/test_issue0725.py | 44 ++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + create mode 100644 tests/issues/test_issue0725.py + +diff --git a/tests/issues/test_issue0725.py b/tests/issues/test_issue0725.py +new file mode 100644 +index 0000000..7479f59 +--- /dev/null ++++ b/tests/issues/test_issue0725.py +@@ -0,0 +1,44 @@ ++# -*- coding: UTF-8 -*- ++""" ++https://github.com/behave/behave/issues/725 ++ ++ANALYSIS: ++---------- ++ ++ScenarioOutlineBuilder did not copy ScenarioOutline.description ++to the Scenarios that were created from the ScenarioOutline. ++""" ++ ++from __future__ import absolute_import, print_function ++from behave.parser import parse_feature ++ ++ ++def test_issue(): ++ """Verifies that issue #725 is fixed.""" ++ text = u''' ++Feature: ScenarioOutline with description ++ ++ Scenario Outline: SO_1 ++ Description line 1 for ScenarioOutline. ++ Description line 2 for ScenarioOutline. ++ ++ Given a step with "" ++ ++ Examples: ++ | name | ++ | Alice | ++ | Bob | ++'''.lstrip() ++ feature = parse_feature(text) ++ assert len(feature.scenarios) == 1 ++ scenario_outline_1 = feature.scenarios[0] ++ assert len(scenario_outline_1.scenarios) == 2 ++ # -- HINT: Last line triggers building of the Scenarios from ScenarioOutline. ++ ++ expected_description = [ ++ "Description line 1 for ScenarioOutline.", ++ "Description line 2 for ScenarioOutline.", ++ ] ++ assert scenario_outline_1.description == expected_description ++ assert scenario_outline_1.scenarios[0].description == expected_description ++ assert scenario_outline_1.scenarios[1].description == expected_description diff --git a/meta-python/recipes-devtools/python/python3-behave/0004-FIX-SummaryReporter-counts-computation-when-Rules-ar.patch b/meta-python/recipes-devtools/python/python3-behave/0004-FIX-SummaryReporter-counts-computation-when-Rules-ar.patch new file mode 100644 index 000000000..ec55efb50 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0004-FIX-SummaryReporter-counts-computation-when-Rules-ar.patch @@ -0,0 +1,342 @@ +From 74d539b86ca52e83255183d96b93ff7492751b6f Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Wed, 13 Mar 2019 08:29:02 +0100 +Subject: [PATCH] FIX: SummaryReporter counts computation when Rules are used. + +--- + behave/model.py | 39 +++--- + behave/reporter/summary.py | 114 ++++++++++++++---- + .../unit/{reporters => reporter}/__init__.py | 0 + .../{reporters => reporter}/test_summary.py | 4 + + 4 files changed, 116 insertions(+), 41 deletions(-) + rename tests/unit/{reporters => reporter}/__init__.py (100%) + rename tests/unit/{reporters => reporter}/test_summary.py (99%) + +diff --git a/behave/model.py b/behave/model.py +index 9dd68fd..6238313 100644 +--- a/behave/model.py ++++ b/behave/model.py +@@ -144,18 +144,18 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + self.hook_failed = False + self.run_starttime = 0 + self.run_endtime = 0 +- for scenario in self.scenarios: +- scenario.reset() ++ for run_item in self.run_items: ++ run_item.reset() + + def __iter__(self): +- return iter(self.scenarios) ++ return iter(self.run_items) + + def add_scenario(self, scenario): + feature = getattr(self, "feature", None) + if isinstance(self, Feature): + feature = self + +- scenario.parent = self # XXX-NEW ++ scenario.parent = self + scenario.feature = feature + scenario.background = self.background + self.scenarios.append(scenario) +@@ -174,17 +174,17 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + + skipped = True + passed_count = 0 +- for scenario in self.scenarios: +- scenario_status = scenario.status +- if scenario_status == Status.failed: ++ for run_item in self.run_items: ++ run_item_status = run_item.status ++ if run_item_status == Status.failed: + return Status.failed +- elif scenario_status == Status.untested: ++ elif run_item_status == Status.untested: + if passed_count > 0: + return Status.failed # ABORTED: Some passed, now untested. + return Status.untested +- if scenario_status != Status.skipped: ++ if run_item_status != Status.skipped: + skipped = False +- if scenario_status == Status.passed: ++ if run_item_status == Status.passed: + passed_count += 1 + + if skipped: +@@ -217,14 +217,19 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + """ + # TODO: Better use self.run_items + all_scenarios = [] +- for scenario in self.scenarios: +- if isinstance(scenario, ScenarioOutline): +- scenario_outline = scenario ++ # for scenario in self.scenarios: ++ for run_item in self.run_items: ++ if isinstance(run_item, Rule): ++ rule = run_item ++ all_scenarios.extend(rule.walk_scenarios(with_outlines=with_outlines)) ++ if isinstance(run_item, ScenarioOutline): ++ scenario_outline = run_item + if with_outlines: + all_scenarios.append(scenario_outline) + all_scenarios.extend(scenario_outline.scenarios) + else: +- all_scenarios.append(scenario) ++ assert isinstance(run_item, Scenario) ++ all_scenarios.append(run_item) + return all_scenarios + + def should_run(self, config=None): +@@ -285,9 +290,9 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + self.clear_status() + self.should_skip = True + self.skip_reason = reason +- for scenario in self.scenarios: +- scenario.skip(reason, require_not_executed) +- if not self.scenarios: ++ for run_item in self.run_items: ++ run_item.skip(reason, require_not_executed) ++ if not self.run_items: + # -- SPECIAL CASE: Feature without scenarios + self.set_status(Status.skipped) + assert self.status in self.final_status #< skipped, failed or passed. +diff --git a/behave/reporter/summary.py b/behave/reporter/summary.py +index c82daa1..2ccdc8f 100644 +--- a/behave/reporter/summary.py ++++ b/behave/reporter/summary.py +@@ -6,25 +6,52 @@ Provides a summary after each test run. + from __future__ import absolute_import, division, print_function + import sys + from time import time as time_now +-from behave.model import ScenarioOutline ++from behave.model import Rule, ScenarioOutline # MAYBE: Scenario + from behave.model_core import Status + from behave.reporter.base import Reporter + from behave.formatter.base import StreamOpener + + +-# -- DISABLED: optional_steps = ('untested', 'undefined') +-optional_steps = (Status.untested,) # MAYBE: Status.undefined +-status_order = (Status.passed, Status.failed, Status.skipped, ++# --------------------------------------------------------------------------- ++# CONSTANTS: ++# --------------------------------------------------------------------------- ++# -- DISABLED: OPTIONAL_STEPS = ('untested', 'undefined') ++OPTIONAL_STEPS = (Status.untested,) # MAYBE: Status.undefined ++STATUS_ORDER = (Status.passed, Status.failed, Status.skipped, + Status.undefined, Status.untested) + + +-def format_summary(statement_type, summary): ++# --------------------------------------------------------------------------- ++# UTILITY FUNCTIONS: ++# --------------------------------------------------------------------------- ++def pluralize(word, count=1, suffix="s"): ++ if count == 1: ++ return word ++ # -- OTHERWISE: ++ return "{0}{1}".format(word, suffix) ++ ++ ++def compute_summary_sum(summary): ++ """Compute sum of all summary counts (except: all) ++ ++ :param summary: Summary counts (as dict). ++ :return: Sum of all counts (as integer). ++ """ ++ counts_sum = 0 ++ for name, count in summary.items(): ++ if name == "all": ++ continue # IGNORE IT. ++ counts_sum += count ++ return counts_sum ++ ++ ++def format_summary0(statement_type, summary): + parts = [] +- for status in status_order: ++ for status in STATUS_ORDER: + if status.name not in summary: + continue + counts = summary[status.name] +- if status in optional_steps and counts == 0: ++ if status in OPTIONAL_STEPS and counts == 0: + # -- SHOW-ONLY: For relevant counts, suppress: untested items, etc. + continue + +@@ -40,11 +67,23 @@ def format_summary(statement_type, summary): + return ", ".join(parts) + "\n" + + +-def pluralize(word, count=1, suffix="s"): +- if count == 1: +- return word +- # -- OTHERWISE: +- return "{0}{1}".format(word, suffix) ++def format_summary(statement_type, summary): ++ parts = [] ++ for status in STATUS_ORDER: ++ if status.name not in summary: ++ continue ++ counts = summary[status.name] ++ if status in OPTIONAL_STEPS and counts == 0: ++ # -- SHOW-ONLY: For relevant counts, suppress: untested items, etc. ++ continue ++ ++ name = status.name ++ if status.name == "passed": ++ statement = pluralize(statement_type, counts) ++ name = u"%s passed" % statement ++ part = u"%d %s" % (counts, name) ++ parts.append(part) ++ return ", ".join(parts) + "\n" + + + # -- PREPARED: +@@ -60,18 +99,16 @@ def format_summary2(statement_type, summary, end="\n"): + :return: + """ + parts = [] +- counts_sum = 0 +- for status in status_order: ++ for status in STATUS_ORDER: + if status.name not in summary: + continue + counts = summary[status.name] +- if status in optional_steps and counts == 0: ++ if status in OPTIONAL_STEPS and counts == 0: + # -- SHOW-ONLY: For relevant counts, suppress: untested items, etc. + continue +- +- counts_sum += counts + parts.append((status.name, counts)) + ++ counts_sum = summary["all"] + statement = pluralize(statement_type, sum) + parts_text = ", ".join(["{0}: {1}".format(name, value) + for name, value in parts]) +@@ -79,6 +116,9 @@ def format_summary2(statement_type, summary, end="\n"): + count=counts_sum, statement=statement, parts=parts_text, end=end) + + ++# --------------------------------------------------------------------------- ++# REPORTERS: ++# --------------------------------------------------------------------------- + class SummaryReporter(Reporter): + show_failed_scenarios = True + output_stream_name = "stdout" +@@ -88,6 +128,7 @@ class SummaryReporter(Reporter): + stream = getattr(sys, self.output_stream_name, sys.stderr) + self.stream = StreamOpener.ensure_stream_with_encoder(stream) + summary_zero_data = { ++ "all": 0, + Status.passed.name: 0, + Status.failed.name: 0, + Status.skipped.name: 0, +@@ -122,10 +163,22 @@ class SummaryReporter(Reporter): + for scenario in self.failed_scenarios: + stream.write(u" %s %s\n" % (scenario.location, scenario.name)) + ++ def compute_summary_sums(self): ++ """(Re)Compute summary sum of all counts (except: all).""" ++ summaries = [ ++ self.feature_summary, ++ self.rule_summary, ++ self.scenario_summary, ++ self.step_summary ++ ] ++ for summary in summaries: ++ summary["all"] = compute_summary_sum(summary) ++ + def print_summary(self, stream=None, with_duration=True): + if stream is None: + stream = self.stream + ++ self.compute_summary_sums() + stream.write(format_summary("feature", self.feature_summary)) + rules_summary = format_summary("rule", self.rule_summary) + if self.show_rules and not rules_summary.strip().startswith("0"): +@@ -145,13 +198,7 @@ class SummaryReporter(Reporter): + # -- DISCOVER: TEST-RUN started. + self.testrun_started() + +- self.feature_summary[feature.status.name] += 1 +- self.duration += feature.duration +- for scenario in feature: +- if isinstance(scenario, ScenarioOutline): +- self.process_scenario_outline(scenario) +- else: +- self.process_scenario(scenario) ++ self.process_feature(feature) + + def end(self): + self.testrun_finished() +@@ -164,6 +211,25 @@ class SummaryReporter(Reporter): + # -- SHOW SUMMARY COUNTS: + self.print_summary() + ++ def process_run_items_for(self, parent): ++ for run_item in parent: ++ if isinstance(run_item, Rule): ++ self.process_rule(run_item) ++ elif isinstance(run_item, ScenarioOutline): ++ self.process_scenario_outline(run_item) ++ else: ++ # assert isinstance(run_item, Scenario) ++ self.process_scenario(run_item) ++ ++ def process_feature(self, feature): ++ self.duration += feature.duration ++ self.feature_summary[feature.status.name] += 1 ++ self.process_run_items_for(feature) ++ ++ def process_rule(self, rule): ++ self.rule_summary[rule.status.name] += 1 ++ self.process_run_items_for(rule) ++ + def process_scenario(self, scenario): + if scenario.status == Status.failed: + self.failed_scenarios.append(scenario) +diff --git a/tests/unit/reporters/__init__.py b/tests/unit/reporter/__init__.py +similarity index 100% +rename from tests/unit/reporters/__init__.py +rename to tests/unit/reporter/__init__.py +diff --git a/tests/unit/reporters/test_summary.py b/tests/unit/reporter/test_summary.py +similarity index 99% +rename from tests/unit/reporters/test_summary.py +rename to tests/unit/reporter/test_summary.py +index 02154db..97adbb5 100644 +--- a/tests/unit/reporters/test_summary.py ++++ b/tests/unit/reporter/test_summary.py +@@ -120,6 +120,7 @@ class TestSummaryReporter(object): + reporter.end() + + expected = { ++ "all": 5, + Status.passed.name: 2, + Status.failed.name: 1, + Status.skipped.name: 1, +@@ -156,6 +157,7 @@ class TestSummaryReporter(object): + reporter.end() + + expected = { ++ "all": 5, + Status.passed.name: 1, + Status.failed.name: 2, + Status.skipped.name: 1, +@@ -201,6 +203,7 @@ class TestSummaryReporter(object): + reporter.end() + + expected = { ++ "all": 7, + Status.passed.name: 2, + Status.failed.name: 3, + Status.skipped.name: 2, +@@ -241,6 +244,7 @@ class TestSummaryReporter(object): + reporter.end() + + expected = { ++ "all": 5, + Status.passed.name: 2, + Status.failed.name: 1, + Status.skipped.name: 1, diff --git a/meta-python/recipes-devtools/python/python3-behave/0005-SummaryReporter.print_summary-Simplify-if-Rules-are-.patch b/meta-python/recipes-devtools/python/python3-behave/0005-SummaryReporter.print_summary-Simplify-if-Rules-are-.patch new file mode 100644 index 000000000..2cbf02925 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0005-SummaryReporter.print_summary-Simplify-if-Rules-are-.patch @@ -0,0 +1,60 @@ +From db1ead991924fb71d87e02aa43ffa73eae60594e Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Wed, 13 Mar 2019 08:41:37 +0100 +Subject: [PATCH] SummaryReporter.print_summary: Simplify if Rules are used. + +--- + behave/reporter/summary.py | 7 ++++--- + tests/unit/reporter/test_summary.py | 6 +++--- + 2 files changed, 7 insertions(+), 6 deletions(-) + +diff --git a/behave/reporter/summary.py b/behave/reporter/summary.py +index 2ccdc8f..09285ea 100644 +--- a/behave/reporter/summary.py ++++ b/behave/reporter/summary.py +@@ -179,11 +179,12 @@ class SummaryReporter(Reporter): + stream = self.stream + + self.compute_summary_sums() ++ has_rules = (self.rule_summary["all"] > 0) ++ + stream.write(format_summary("feature", self.feature_summary)) +- rules_summary = format_summary("rule", self.rule_summary) +- if self.show_rules and not rules_summary.strip().startswith("0"): ++ if self.show_rules and has_rules: + # -- HINT: Show only rules, if any exists. +- self.stream.write(rules_summary) ++ self.stream.write(format_summary("rule", self.rule_summary)) + stream.write(format_summary("scenario", self.scenario_summary)) + stream.write(format_summary("step", self.step_summary)) + +diff --git a/tests/unit/reporter/test_summary.py b/tests/unit/reporter/test_summary.py +index 97adbb5..d4e85b5 100644 +--- a/tests/unit/reporter/test_summary.py ++++ b/tests/unit/reporter/test_summary.py +@@ -164,7 +164,7 @@ class TestSummaryReporter(object): + Status.untested.name: 1, + } + +- scenario_index = 2 ++ scenario_index = 1 # -- HINT: Index for scenarios if no Rules are used. + expected_parts = ("scenario", expected) + assert format_summary.call_args_list[scenario_index][0] == expected_parts + +@@ -209,7 +209,7 @@ class TestSummaryReporter(object): + Status.skipped.name: 2, + Status.untested.name: 0, + } +- scenario_index = 2 ++ scenario_index = 1 # -- HINT: Index for scenarios if no Rules are used. + expected_parts = ("scenario", expected) + assert format_summary.call_args_list[scenario_index][0] == expected_parts + +@@ -252,6 +252,6 @@ class TestSummaryReporter(object): + Status.undefined.name: 1, + } + +- step_index = 3 ++ step_index = 2 # HINT: Index for steps if not rules are used. + expected_parts = ("step", expected) + assert format_summary.call_args_list[step_index][0] == expected_parts diff --git a/meta-python/recipes-devtools/python/python3-behave/0006-Formatter-Add-basic-support-output-for-Rules.patch b/meta-python/recipes-devtools/python/python3-behave/0006-Formatter-Add-basic-support-output-for-Rules.patch new file mode 100644 index 000000000..bcfe315bf --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0006-Formatter-Add-basic-support-output-for-Rules.patch @@ -0,0 +1,395 @@ +From 24811b631e0eed92347880f1dac3f932f4b46f9d Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Wed, 13 Mar 2019 23:08:19 +0100 +Subject: [PATCH] Formatter: Add basic support/output for Rules. + +--- + behave/formatter/base.py | 18 +++++------ + behave/formatter/plain.py | 63 +++++++++++++++++++++++++++++------- + behave/formatter/pretty.py | 33 +++++++++++++++---- + behave/formatter/progress.py | 18 ++++++++++- + behave/model.py | 14 ++++---- + 5 files changed, 110 insertions(+), 36 deletions(-) + +diff --git a/behave/formatter/base.py b/behave/formatter/base.py +index f7268fa..a8b9f7c 100644 +--- a/behave/formatter/base.py ++++ b/behave/formatter/base.py +@@ -129,15 +129,6 @@ class Formatter(object): + """ + pass + +- def background(self, background): +- """Called when a (Feature) Background is provided. +- Called after :method:`feature()` is called. +- Called before processing any scenarios or scenario outlines. +- +- :param background: Background object (as :class:`behave.model.Background`) +- """ +- pass +- + def rule(self, rule): + """Called before a rule is executed. + +@@ -153,6 +144,15 @@ class Formatter(object): + # """ + # pass + ++ def background(self, background): ++ """Called when a (Feature) Background is provided. ++ Called after :method:`feature()` is called. ++ Called before processing any scenarios or scenario outlines. ++ ++ :param background: Background object (as :class:`behave.model.Background`) ++ """ ++ pass ++ + def scenario(self, scenario): + """Called before a scenario is executed (or ScenarioOutline scenarios). + +diff --git a/behave/formatter/plain.py b/behave/formatter/plain.py +index 9f1f833..e720829 100644 +--- a/behave/formatter/plain.py ++++ b/behave/formatter/plain.py +@@ -23,6 +23,8 @@ class PlainFormatter(Formatter): + + SHOW_MULTI_LINE = True + SHOW_TAGS = False ++ SHOW_RULES = True ++ SHOW_BACKGROUNDS = True + SHOW_ALIGNED_KEYWORDS = False + DEFAULT_INDENT_SIZE = 2 + RAISE_OUTPUT_ERRORS = True +@@ -35,6 +37,7 @@ class PlainFormatter(Formatter): + self.show_aligned_keywords = self.SHOW_ALIGNED_KEYWORDS + self.show_tags = self.SHOW_TAGS + self.indent_size = self.DEFAULT_INDENT_SIZE ++ self.current_rule = None + # -- ENSURE: Output stream is open. + self.stream = self.open() + self.printer = ModelPrinter(self.stream) +@@ -49,6 +52,10 @@ class PlainFormatter(Formatter): + offset = 2 + indentation = make_indentation(3 * self.indent_size + offset) + self._multiline_indentation = indentation ++ ++ if self.current_rule: ++ indent_extra = make_indentation(self.indent_size) ++ return self._multiline_indentation + indent_extra + return self._multiline_indentation + + def reset_steps(self): +@@ -60,37 +67,69 @@ class PlainFormatter(Formatter): + text = " @".join(tags) + self.stream.write(u"%s@%s\n" % (indent, text)) + ++ def write_entity(self, entity, indent="", has_tags=True): ++ if has_tags: ++ self.write_tags(entity.tags, indent) ++ text = u"%s%s: %s\n" % (indent, entity.keyword, entity.name) ++ self.stream.write(text) ++ + # -- IMPLEMENT-INTERFACE FOR: Formatter + def feature(self, feature): ++ self.current_rule = None + self.reset_steps() +- self.write_tags(feature.tags) +- self.stream.write(u"%s: %s\n" % (feature.keyword, feature.name)) ++ self.write_entity(feature) ++ # self.write_tags(feature.tags) ++ # self.stream.write(u"%s: %s\n" % (feature.keyword, feature.name)) + +- def background(self, background): ++ def rule(self, rule): ++ self.current_rule = rule + self.reset_steps() + indent = make_indentation(self.indent_size) +- text = u"%s%s: %s\n" % (indent, background.keyword, background.name) +- self.stream.write(text) ++ self.stream.write(u"\n") ++ self.write_entity(rule, indent) ++ # self.stream.write(u"%s%s: %s\n" % (indent, rule.keyword, rule.name)) ++ ++ def background(self, background): ++ self.reset_steps() ++ if not self.SHOW_BACKGROUNDS: ++ return ++ ++ indent_extra = 0 ++ if self.current_rule: ++ indent_extra = self.indent_size ++ ++ indent = make_indentation(self.indent_size + indent_extra) ++ self.write_entity(background, indent, has_tags=False) ++ # text = u"%s%s: %s\n" % (indent, background.keyword, background.name) ++ # self.stream.write(text) + + def scenario(self, scenario): ++ indent_extra = 0 ++ if self.current_rule: ++ indent_extra = self.indent_size ++ + self.reset_steps() + self.stream.write(u"\n") +- indent = make_indentation(self.indent_size) +- text = u"%s%s: %s\n" % (indent, scenario.keyword, scenario.name) +- self.write_tags(scenario.tags, indent) +- self.stream.write(text) ++ indent = make_indentation(self.indent_size + indent_extra) ++ self.write_entity(scenario, indent) ++ # text = u"%s%s: %s\n" % (indent, scenario.keyword, scenario.name) ++ # self.write_tags(scenario.tags, indent) ++ # self.stream.write(text) + + def step(self, step): + self.steps.append(step) + + def result(self, step): +- """ +- Process the result of a step (after step execution). ++ """Process the result of a step (after step execution). + + :param step: Step object with result to process. + """ ++ indent_extra = 0 ++ if self.current_rule: ++ indent_extra = self.indent_size ++ + step = self.steps.pop(0) +- indent = make_indentation(2 * self.indent_size) ++ indent = make_indentation(2 * self.indent_size + indent_extra) + if self.show_aligned_keywords: + # -- RIGHT-ALIGN KEYWORDS (max. keyword width: 6): + text = u"%s%6s %s ... " % (indent, step.keyword, step.name) +diff --git a/behave/formatter/pretty.py b/behave/formatter/pretty.py +index b6f0eac..794e1d7 100644 +--- a/behave/formatter/pretty.py ++++ b/behave/formatter/pretty.py +@@ -6,7 +6,7 @@ from behave.formatter.ansi_escapes import escapes, up + from behave.formatter.base import Formatter + from behave.model_core import Status + from behave.model_describe import escape_cell, escape_triple_quotes +-from behave.textutil import indent, text as _text ++from behave.textutil import indent, make_indentation, text as _text + import six + from six.moves import range, zip + +@@ -86,6 +86,7 @@ class PrettyFormatter(Formatter): + + def reset(self): + # -- UNUSED: self.tag_statement = None ++ self.current_rule = None + self.steps = [] + self._uri = None + self._match = None +@@ -99,7 +100,9 @@ class PrettyFormatter(Formatter): + + def feature(self, feature): + #self.print_comments(feature.comments, '') +- self.print_tags(feature.tags, '') ++ self.current_rule = None ++ prefix = "" ++ self.print_tags(feature.tags, prefix) + self.stream.write(u"%s: %s" % (feature.keyword, feature.name)) + if self.show_source: + # pylint: disable=redefined-builtin +@@ -109,6 +112,11 @@ class PrettyFormatter(Formatter): + self.print_description(feature.description, " ", False) + self.stream.flush() + ++ def rule(self, rule): ++ self.replay() ++ self.current_rule = rule ++ self.statement = rule ++ + def background(self, background): + self.replay() + self.statement = background +@@ -176,6 +184,10 @@ class PrettyFormatter(Formatter): + self.stream.flush() + + def table(self, table): ++ prefix = u" " ++ if self.current_rule: ++ prefix += u" " ++ + cell_lengths = [] + all_rows = [table.headings] + table.rows + for row in all_rows: +@@ -189,7 +201,7 @@ class PrettyFormatter(Formatter): + for i, row in enumerate(all_rows): + #for comment in row.comments: + # self.stream.write(" %s\n" % comment.value) +- self.stream.write(" |") ++ self.stream.write(u"%s|" % prefix) + for j, (cell, max_length) in enumerate(zip(row, max_lengths)): + self.stream.write(" ") + self.stream.write(self.color(cell, None, j)) +@@ -202,6 +214,8 @@ class PrettyFormatter(Formatter): + #self.stream.write(' """' + doc_string.content_type + '\n') + doc_string = _text(doc_string) + prefix = u" " ++ if self.current_rule: ++ prefix += u" " + self.stream.write(u'%s"""\n' % prefix) + doc_string = escape_triple_quotes(indent(doc_string, prefix)) + self.stream.write(doc_string) +@@ -251,12 +265,16 @@ class PrettyFormatter(Formatter): + if self.statement is None: + return + ++ prefix = u" " ++ if self.current_rule and self.statement.type != "rule": ++ prefix += prefix ++ + self.calculate_location_indentations() + self.stream.write(u"\n") + #self.print_comments(self.statement.comments, " ") + if hasattr(self.statement, "tags"): +- self.print_tags(self.statement.tags, u" ") +- self.stream.write(u" %s: %s " % (self.statement.keyword, ++ self.print_tags(self.statement.tags, prefix) ++ self.stream.write(u"%s%s: %s " % (prefix, self.statement.keyword, + self.statement.name)) + + location = self.indented_text(six.text_type(self.statement.location), True) +@@ -279,8 +297,11 @@ class PrettyFormatter(Formatter): + text_format = self.format(status.name) + arg_format = self.arg_format(status.name) + ++ prefix = u" " ++ if self.current_rule: ++ prefix += u" " + #self.print_comments(step.comments, " ") +- self.stream.write(" ") ++ self.stream.write(prefix) + self.stream.write(text_format.text(step.keyword + " ")) + line_length = 5 + len(step.keyword) + +diff --git a/behave/formatter/progress.py b/behave/formatter/progress.py +index 6d8adf6..3b471ed 100644 +--- a/behave/formatter/progress.py ++++ b/behave/formatter/progress.py +@@ -43,6 +43,7 @@ class ProgressFormatterBase(Formatter): + self.steps = [] + self.failures = [] + self.current_feature = None ++ self.current_rule = None + self.current_scenario = None + self.show_timings = config.show_timings and self.show_timings + +@@ -50,14 +51,19 @@ class ProgressFormatterBase(Formatter): + self.steps = [] + self.failures = [] + self.current_feature = None ++ self.current_rule = None + self.current_scenario = None + + # -- FORMATTER API: + def feature(self, feature): ++ self.current_rule = None + self.current_feature = feature + self.stream.write("%s " % six.text_type(feature.filename)) + self.stream.flush() + ++ def rule(self, rule): ++ self.current_rule = rule ++ + def background(self, background): + pass + +@@ -219,9 +225,16 @@ class ScenarioStepProgressFormatter(StepProgressFormatter): + + # -- FORMATTER API: + def feature(self, feature): ++ self.current_rule = None + self.current_feature = feature + self.stream.write(u"%s # %s" % (feature.name, feature.filename)) + ++ def rule(self, rule): ++ self.current_rule = rule ++ self.stream.write(u"\n\n %s: %s # %s" % ++ (rule.keyword, rule.name, rule.location)) ++ self.stream.flush() ++ + def scenario(self, scenario): + """Process the next scenario.""" + # -- LAST SCENARIO: Report failures (if any). +@@ -231,9 +244,12 @@ class ScenarioStepProgressFormatter(StepProgressFormatter): + assert not self.failures + self.current_scenario = scenario + scenario_name = scenario.name ++ prefix = self.scenario_prefix ++ if self.current_rule: ++ prefix += u" " + if scenario_name: + scenario_name += " " +- self.stream.write(u"%s%s " % (self.scenario_prefix, scenario_name)) ++ self.stream.write(u"%s%s " % (prefix, scenario_name)) + self.stream.flush() + + # -- DISABLED: +diff --git a/behave/model.py b/behave/model.py +index 6238313..3084850 100644 +--- a/behave/model.py ++++ b/behave/model.py +@@ -318,10 +318,10 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + runner.context.tags = set(self.tags) + + skip_entity_untested = runner.aborted +- run_entity = self.should_run(runner.config) ++ should_run_entity = self.should_run(runner.config) + failed_count = 0 + hooks_called = False +- if not runner.config.dry_run and run_entity: ++ if not runner.config.dry_run and should_run_entity: + hooks_called = True + for tag in self.tags: + runner.run_hook("before_tag", runner.context, tag) +@@ -332,10 +332,10 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + # -- RE-EVALUATE SHOULD-RUN STATE: + # Hook may call entity.mark_skipped() to exclude it. + skip_entity_untested = self.hook_failed or runner.aborted +- run_entity = self.should_run() ++ should_run_entity = self.should_run() + + # run this entity if the tags say so or any one of its scenarios +- if run_entity or runner.config.show_skipped: ++ if should_run_entity or runner.config.show_skipped: + for formatter in runner.formatters: + formatter_callback = getattr(formatter, entity_name, None) + if formatter_callback: +@@ -363,7 +363,7 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + break + + self.clear_status() # -- ENFORCE: compute_status() after run. +- if not self.run_items and not run_entity: ++ if not self.run_items and not should_run_entity: + # -- SPECIAL CASE: Feature without scenarios + self.set_status(Status.skipped) + +@@ -382,7 +382,7 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + # -- CLEANUP-ERROR: + self.set_status(Status.failed) + +- if run_entity or runner.config.show_skipped: ++ if should_run_entity or runner.config.show_skipped: + callback_name = "{0}_finished".format(entity_name) + if entity_name == "feature": + callback_name = "eof" +@@ -608,7 +608,6 @@ class Rule(ScenarioContainer): + .. versionadded:: 1.2.7 + .. _`feature`: gherkin.html#rule + """ +- + type = "rule" + + def __init__(self, filename, line, keyword, name, tags=None, +@@ -625,7 +624,6 @@ class Rule(ScenarioContainer): + (self.name, len(self.scenarios)) + + +- + class Background(BasicStatement, Replayable): + """A `background`_ parsed from a *feature file*. + diff --git a/meta-python/recipes-devtools/python/python3-behave/0007-BUMP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch b/meta-python/recipes-devtools/python/python3-behave/0007-BUMP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch new file mode 100644 index 000000000..b8e804269 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0007-BUMP-VERSION-1.2.7.dev1-was-1.2.7.dev0.patch @@ -0,0 +1,67 @@ +From 19a4134596217540832ed394d790d7b509ec865a Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Wed, 13 Mar 2019 23:11:50 +0100 +Subject: [PATCH] BUMP-VERSION: 1.2.7.dev1 (was: 1.2.7.dev0) + +--- + .bumpversion.cfg | 2 +- + VERSION.txt | 2 +- + behave/__init__.py | 2 +- + pytest.ini | 2 +- + setup.py | 2 +- + 5 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/.bumpversion.cfg b/.bumpversion.cfg +index f387d43..ac913c2 100644 +--- a/.bumpversion.cfg ++++ b/.bumpversion.cfg +@@ -1,5 +1,5 @@ + [bumpversion] +-current_version = 1.2.7.dev0 ++current_version = 1.2.7.dev1 + files = behave/__init__.py setup.py VERSION.txt pytest.ini .bumpversion.cfg + parse = (?P\d+)\.(?P\d+)\.(?P\d+)(?P\w*) + serialize = {major}.{minor}.{patch}{drop} +diff --git a/VERSION.txt b/VERSION.txt +index 4e63eef..c0ef36b 100644 +--- a/VERSION.txt ++++ b/VERSION.txt +@@ -1 +1 @@ +-1.2.7.dev0 ++1.2.7.dev1 +diff --git a/behave/__init__.py b/behave/__init__.py +index 8888355..31e4e55 100644 +--- a/behave/__init__.py ++++ b/behave/__init__.py +@@ -29,4 +29,4 @@ __all__ = [ + # -- DEPRECATING: + "step_matcher" + ] +-__version__ = "1.2.7.dev0" ++__version__ = "1.2.7.dev1" +diff --git a/pytest.ini b/pytest.ini +index 70e10cd..17ad388 100644 +--- a/pytest.ini ++++ b/pytest.ini +@@ -20,7 +20,7 @@ minversion = 2.8 + testpaths = test tests + python_files = test_*.py + addopts = --metadata PACKAGE_UNDER_TEST behave +- --metadata PACKAGE_VERSION 1.2.7.dev0 ++ --metadata PACKAGE_VERSION 1.2.7.dev1 + --html=build/testing/report.html --self-contained-html + --junit-xml=build/testing/report.xml + markers = +diff --git a/setup.py b/setup.py +index cb3b338..c5af262 100644 +--- a/setup.py ++++ b/setup.py +@@ -55,7 +55,7 @@ def find_packages_by_root_package(where): + # ----------------------------------------------------------------------------- + setup( + name="behave", +- version="1.2.7.dev0", ++ version="1.2.7.dev1", + description="behave is behaviour-driven development, Python style", + long_description=description, + author="Jens Engel, Benno Rice and Richard Jones", diff --git a/meta-python/recipes-devtools/python/python3-behave/0008-Correct-examples-and-docstring.patch b/meta-python/recipes-devtools/python/python3-behave/0008-Correct-examples-and-docstring.patch new file mode 100644 index 000000000..9ab6cee15 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0008-Correct-examples-and-docstring.patch @@ -0,0 +1,41 @@ +From 29d3ef4d3ff8c836bc592b92687a28bf873d0e0c Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Thu, 14 Mar 2019 22:14:54 +0100 +Subject: [PATCH] Correct examples and docstring + +--- + behave/contrib/scenario_autoretry.py | 2 +- + behave/formatter/base.py | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/behave/contrib/scenario_autoretry.py b/behave/contrib/scenario_autoretry.py +index 2b7f94f..2592d10 100644 +--- a/behave/contrib/scenario_autoretry.py ++++ b/behave/contrib/scenario_autoretry.py +@@ -24,7 +24,7 @@ EXAMPLE: + from behave.contrib.scenario_autoretry import patch_scenario_with_autoretry + + def before_feature(context, feature): +- for scenario in feature.scenarios: ++ for scenario in feature.walk_scenarios(): + if "autoretry" in scenario.effective_tags: + patch_scenario_with_autoretry(scenario, max_attempts=2) + +diff --git a/behave/formatter/base.py b/behave/formatter/base.py +index a8b9f7c..7f59ad4 100644 +--- a/behave/formatter/base.py ++++ b/behave/formatter/base.py +@@ -74,11 +74,12 @@ class Formatter(object): + + Processing Logic (simplified, without ScenarioOutline and skip logic):: + ++ # -- HINT: Rule processing is missing. + for feature in runner.features: + formatter = make_formatters(...) + formatter.uri(feature.filename) + formatter.feature(feature) +- for scenario in feature.scenarios: ++ for scenario in feature.walk_scenarios(): + formatter.scenario(scenario) + for step in scenario.all_steps: + formatter.step(step) diff --git a/meta-python/recipes-devtools/python/python3-behave/0009-FIX-feature.run_items-processing-with-Rule-s.patch b/meta-python/recipes-devtools/python/python3-behave/0009-FIX-feature.run_items-processing-with-Rule-s.patch new file mode 100644 index 000000000..0c70cc566 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0009-FIX-feature.run_items-processing-with-Rule-s.patch @@ -0,0 +1,58 @@ +From dee5266820aabcfe09d103cf007bb26b9db54849 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Thu, 14 Mar 2019 22:15:34 +0100 +Subject: [PATCH] FIX: feature.run_items processing with Rule(s). + +--- + behave/reporter/junit.py | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/behave/reporter/junit.py b/behave/reporter/junit.py +index 48e1411..9018399 100644 +--- a/behave/reporter/junit.py ++++ b/behave/reporter/junit.py +@@ -75,7 +75,7 @@ import codecs + from xml.etree import ElementTree + from datetime import datetime + from behave.reporter.base import Reporter +-from behave.model import Scenario, ScenarioOutline, Step ++from behave.model import Rule, Scenario, ScenarioOutline, Step + from behave.model_core import Status + from behave.formatter import ansi_escapes + from behave.model_describe import ModelDescriptor +@@ -236,13 +236,8 @@ class JUnitReporter(Reporter): + feature_name = feature.name or feature_filename + suite.set(u'name', u'%s.%s' % (classname, feature_name)) + +- # -- BUILD-TESTCASES: From scenarios +- for scenario in feature: +- if isinstance(scenario, ScenarioOutline): +- scenario_outline = scenario +- self._process_scenario_outline(scenario_outline, report) +- else: +- self._process_scenario(scenario, report) ++ # -- BUILD-TESTCASES: From run_items (and scenarios) ++ self._process_run_items_for(feature, report) + + # -- ADD TESTCASES to testsuite: + for testcase in report.testcases: +@@ -457,6 +452,19 @@ class JUnitReporter(Reporter): + if scenario.status != Status.skipped or self.show_skipped: + report.testcases.append(case) + ++ def _process_run_items_for(self, parent, report): ++ for run_item in parent.run_items: ++ if isinstance(run_item, Rule): ++ self._process_rule(run_item, report) ++ elif isinstance(run_item, ScenarioOutline): ++ self._process_scenario_outline(run_item, report) ++ else: ++ assert isinstance(run_item, Scenario) ++ self._process_scenario(run_item, report) ++ ++ def _process_rule(self, rule, report): ++ self._process_run_items_for(rule, report) ++ + def _process_scenario_outline(self, scenario_outline, report): + assert isinstance(scenario_outline, ScenarioOutline) + for scenario in scenario_outline: diff --git a/meta-python/recipes-devtools/python/python3-behave/0010-docs-Disable-sphinx-intl-support-for-READTHEDOCS.patch b/meta-python/recipes-devtools/python/python3-behave/0010-docs-Disable-sphinx-intl-support-for-READTHEDOCS.patch new file mode 100644 index 000000000..122b7e9f2 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0010-docs-Disable-sphinx-intl-support-for-READTHEDOCS.patch @@ -0,0 +1,58 @@ +From b4a40c4df5872b0c9c7293b3a9fa057e208361d6 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 26 May 2019 14:40:38 +0200 +Subject: [PATCH] docs: Disable sphinx-intl support for READTHEDOCS. + +--- + docs/conf.py | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/docs/conf.py b/docs/conf.py +index d38db7a..f9dfb6a 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -3,6 +3,7 @@ + # SPHINX CONFIGURATION: behave documentation build configuration file + # ============================================================================= + ++from __future__ import print_function + import os.path + import sys + import importlib +@@ -13,6 +14,13 @@ import importlib + # documentation root, use os.path.abspath to make it absolute, like shown here. + sys.path.insert(0, os.path.abspath("..")) + ++# ------------------------------------------------------------------------------ ++# DETECT BUILD CONTEXT ++# ------------------------------------------------------------------------------ ++ON_READTHEDOCS = os.environ.get("READTHEDOCS", None) == "True" ++USE_SPHINX_INTERNATIONAL = not ON_READTHEDOCS ++ ++ + # ------------------------------------------------------------------------------ + # EXTENSIONS CONFIGURATION + # ------------------------------------------------------------------------------ +@@ -82,8 +90,10 @@ master_doc = "index" + # -- MULTI-LANGUAGE SUPPORT: en, ... + # SEE: https://pypi.org/project/sphinx-intl/ + # SEE: https://github.com/sphinx-doc/sphinx-intl/ +-locale_dirs = ["locale/"] # path is example but recommended. +-gettext_compact = False # optional. ++if USE_SPHINX_INTERNATIONAL: ++ locale_dirs = ["locale/"] # path is example but recommended. ++ gettext_compact = False # optional. ++ print("USE SPHINX-INTL: locale_dirs=%s" % ",".join(locale_dirs)) + + # STEPS: + # make gettext +@@ -155,8 +165,7 @@ todo_include_todos = False + html_theme = "kr" + html_theme = "bootstrap" + +-on_rtd = os.environ.get("READTHEDOCS", None) == "True" +-if on_rtd: ++if ON_READTHEDOCS: + html_theme = "default" + + if html_theme == "bootstrap": diff --git a/meta-python/recipes-devtools/python/python3-behave/0011-Cleanup-Dependent-package-versions-in-requirements.patch b/meta-python/recipes-devtools/python/python3-behave/0011-Cleanup-Dependent-package-versions-in-requirements.patch new file mode 100644 index 000000000..726402a8e --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0011-Cleanup-Dependent-package-versions-in-requirements.patch @@ -0,0 +1,81 @@ +From ffbd9840d2c2e273a0ce2ea8fe20afad034bdeb2 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Thu, 18 Apr 2019 19:02:43 +0200 +Subject: [PATCH] Cleanup: Dependent package versions in requirements. + +--- + py.requirements/basic.txt | 2 +- + py.requirements/develop.txt | 4 ++-- + py.requirements/testing.txt | 2 +- + setup.py | 6 +++--- + 4 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/py.requirements/basic.txt b/py.requirements/basic.txt +index 9eebcad..3b71bfb 100644 +--- a/py.requirements/basic.txt ++++ b/py.requirements/basic.txt +@@ -11,7 +11,7 @@ + cucumber-tag-expressions >= 1.1.1 + parse >= 1.8.2 + parse_type >= 0.4.2 +-six >= 1.11.0 ++six >= 1.12.0 + + traceback2; python_version < '3.0' + contextlib2 # MAYBE: python_version < '3.5' +diff --git a/py.requirements/develop.txt b/py.requirements/develop.txt +index c55d3cd..3deedc7 100644 +--- a/py.requirements/develop.txt ++++ b/py.requirements/develop.txt +@@ -5,8 +5,8 @@ + # -- BUILD-TOOL: + # PREPARE USAGE: invoke + # ALREADY: six >= 1.11.0 +-invoke >= 0.21.0 +-path.py >= 10.1 ++invoke >= 1.2.0 ++path.py >= 11.5.0 + pathlib; python_version <= '3.4' + pycmd + +diff --git a/py.requirements/testing.txt b/py.requirements/testing.txt +index 5876e29..3806d39 100644 +--- a/py.requirements/testing.txt ++++ b/py.requirements/testing.txt +@@ -12,4 +12,4 @@ PyHamcrest >= 1.9 + + # -- NEEDED: By some tests (as proof of concept) + # NOTE: path.py-10.1 is required for python2.6 +-path.py >= 10.1 ++path.py >= 11.5.0 +diff --git a/setup.py b/setup.py +index c5af262..ac7bddf 100644 +--- a/setup.py ++++ b/setup.py +@@ -79,7 +79,7 @@ setup( + "cucumber-tag-expressions >= 1.1.1", + "parse >= 1.8.2", + "parse_type >= 0.4.2", +- "six >= 1.11.0", ++ "six >= 1.12.0", + "traceback2; python_version < '3.0'", + "enum34; python_version < '3.4'", + # -- PREPARED: +@@ -93,7 +93,7 @@ setup( + "nose >= 1.3", + "mock >= 1.1", + "PyHamcrest >= 1.8", +- "path.py >= 10.1" ++ "path.py >= 11.5.0" + ], + cmdclass = { + "behave_test": behave_test, +@@ -110,7 +110,7 @@ setup( + "pytest-cov", + "tox", + "invoke >= 1.2.0", +- "path.py >= 10.1", ++ "path.py >= 11.5.0", + "pycmd", + "pathlib; python_version <= '3.4'", + "modernize >= 0.5", diff --git a/meta-python/recipes-devtools/python/python3-behave/0012-docs-conf.py-tweaks.patch b/meta-python/recipes-devtools/python/python3-behave/0012-docs-conf.py-tweaks.patch new file mode 100644 index 000000000..22c6c6bb6 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0012-docs-conf.py-tweaks.patch @@ -0,0 +1,30 @@ +From 430d19123b3a7adc21075b1befda8b550b7eb641 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 26 May 2019 14:55:20 +0200 +Subject: [PATCH] docs: conf.py tweaks. + +--- + docs/conf.py | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/docs/conf.py b/docs/conf.py +index f9dfb6a..f7c2c24 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -18,7 +18,7 @@ sys.path.insert(0, os.path.abspath("..")) + # DETECT BUILD CONTEXT + # ------------------------------------------------------------------------------ + ON_READTHEDOCS = os.environ.get("READTHEDOCS", None) == "True" +-USE_SPHINX_INTERNATIONAL = not ON_READTHEDOCS ++USE_SPHINX_INTERNATIONAL = True + + + # ------------------------------------------------------------------------------ +@@ -164,7 +164,6 @@ todo_include_todos = False + # a list of builtin themes. + html_theme = "kr" + html_theme = "bootstrap" +- + if ON_READTHEDOCS: + html_theme = "default" + diff --git a/meta-python/recipes-devtools/python/python3-behave/0013-FIX-Misspelled-after_rule-hook-was-after_after.patch b/meta-python/recipes-devtools/python/python3-behave/0013-FIX-Misspelled-after_rule-hook-was-after_after.patch new file mode 100644 index 000000000..aa67bd2f4 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0013-FIX-Misspelled-after_rule-hook-was-after_after.patch @@ -0,0 +1,30 @@ +From 5ea1f1b47c14cf9aeabe7d8e22511d54b15e5f1e Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 8 Jun 2019 20:11:23 +0200 +Subject: [PATCH] FIX: Misspelled after_rule hook (was: after_after) + +--- + docs/context_attributes.rst | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/docs/context_attributes.rst b/docs/context_attributes.rst +index a4817d1..163664b 100644 +--- a/docs/context_attributes.rst ++++ b/docs/context_attributes.rst +@@ -23,6 +23,7 @@ config test run :class:`~behave.configuration.Configuration` Configur + aborted test run bool Set to true if test run is aborted by the user. + failed test run bool Set to true if a step fails. + feature feature :class:`~behave.model.Feature` Current feature. ++rule rule :class:`~behave.model.Feature` Current rule. + tags feature, list<:class:`~behave.model.Tag`> Effective tags of current feature, rule, scenario, scenario outline. + rule, + scenario +@@ -62,7 +63,7 @@ Hook :func:`after_tags` feature, rule or scenario + Hook :func:`before_feature` feature + Hook :func:`after_feature` feature + Hook :func:`before_rule` rule +-Hook :func:`after_after` rule ++Hook :func:`after_rule` rule + Hook :func:`before_scenario` scenario + Hook :func:`after_scenario` scenario + Hook :func:`before_step` scenario diff --git a/meta-python/recipes-devtools/python/python3-behave/0014-Add-hints-on-Gherkin-v6-grammar-issues.patch b/meta-python/recipes-devtools/python/python3-behave/0014-Add-hints-on-Gherkin-v6-grammar-issues.patch new file mode 100644 index 000000000..7322216cc --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0014-Add-hints-on-Gherkin-v6-grammar-issues.patch @@ -0,0 +1,45 @@ +From 14e4c88e9bd1a12a2f081dfb2709df9f78106ca6 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 8 Jun 2019 20:12:23 +0200 +Subject: [PATCH] Add hints on Gherkin v6 grammar issues. + +--- + docs/conf.py | 4 +++- + docs/new_and_noteworthy_v1.2.7.rst | 8 ++++++++ + 2 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/docs/conf.py b/docs/conf.py +index f7c2c24..e55fb21 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -54,9 +54,11 @@ for optional_module_name in optional_extensions: + extlinks = { + "pypi": ("https://pypi.org/project/%s", ""), + "github": ("https://github.com/%s", "github:/"), +- "issue": ("https://github.com/behave/behave/issue/%s", "issue #"), ++ "issue": ("https://github.com/behave/behave/issues/%s", "issue #"), + "youtube": ("https://www.youtube.com/watch?v=%s", "youtube:video="), + "behave": ("https://github.com/behave/behave", None), ++ "cucumber": ("https://github.com/cucumber/cucumber/", None), ++ "cucumber.issue": ("https://github.com/cucumber/cucumber/issues/%s", "issue #"), + } + + intersphinx_mapping = { +diff --git a/docs/new_and_noteworthy_v1.2.7.rst b/docs/new_and_noteworthy_v1.2.7.rst +index 451ed8c..80d9576 100644 +--- a/docs/new_and_noteworthy_v1.2.7.rst ++++ b/docs/new_and_noteworthy_v1.2.7.rst +@@ -92,5 +92,13 @@ Overview of the `Example Mapping`_ concepts: + * https://lisacrispin.com/2016/06/02/experiment-example-mapping/ + * https://tobythetesterblog.wordpress.com/2016/05/25/how-to-do-example-mapping/ + ++.. hint:: **Gherkin v6 Grammar Issues** ++ ++ * :cucumber.issue:`632`: Rule tags are currently only supported in `behave`. ++ The Cucumber Gherkin v6 grammar currently lacks this functionality. ++ ++ * :cucumber.issue:`590`: Rule Background: ++ A proposal is pending to remove Rule Backgrounds again ++ + + .. include:: _content.tag_expressions_v2.rst diff --git a/meta-python/recipes-devtools/python/python3-behave/0015-README-ReST-tweaks.patch b/meta-python/recipes-devtools/python/python3-behave/0015-README-ReST-tweaks.patch new file mode 100644 index 000000000..04e45f3f3 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0015-README-ReST-tweaks.patch @@ -0,0 +1,18 @@ +From 12d37ec6af46d3edb806679183ab2138e4f1f5bf Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 8 Jun 2019 20:13:08 +0200 +Subject: [PATCH] README: ReST tweaks + +--- + etc/gherkin/README.rst | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/etc/gherkin/README.rst b/etc/gherkin/README.rst +index ad3cedb..7ec2108 100644 +--- a/etc/gherkin/README.rst ++++ b/etc/gherkin/README.rst +@@ -1,3 +1,4 @@ + SOURCE: ++ + * https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json + * https://raw.githubusercontent.com/cucumber/cucumber/master/gherkin/gherkin-languages.json diff --git a/meta-python/recipes-devtools/python/python3-behave/0016-Example-using-Gherkin-v6-grammar.patch b/meta-python/recipes-devtools/python/python3-behave/0016-Example-using-Gherkin-v6-grammar.patch new file mode 100644 index 000000000..cbbcfd541 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0016-Example-using-Gherkin-v6-grammar.patch @@ -0,0 +1,228 @@ +From eabcf73f0e7f26ef021cef30950c7bb3d2442226 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 8 Jun 2019 20:16:01 +0200 +Subject: [PATCH] Example using Gherkin v6 grammar. + +--- + examples/gherkin_v6/README.rst | 18 ++++++++ + examples/gherkin_v6/behave.ini | 42 +++++++++++++++++++ + examples/gherkin_v6/features/rule_1.feature | 42 +++++++++++++++++++ + examples/gherkin_v6/features/rule_2.feature | 42 +++++++++++++++++++ + .../features/steps/example_steps.py | 21 ++++++++++ + .../gherkin_v6/features/steps/person_steps.py | 7 ++++ + 6 files changed, 172 insertions(+) + create mode 100644 examples/gherkin_v6/README.rst + create mode 100644 examples/gherkin_v6/behave.ini + create mode 100644 examples/gherkin_v6/features/rule_1.feature + create mode 100644 examples/gherkin_v6/features/rule_2.feature + create mode 100644 examples/gherkin_v6/features/steps/example_steps.py + create mode 100644 examples/gherkin_v6/features/steps/person_steps.py + +diff --git a/examples/gherkin_v6/README.rst b/examples/gherkin_v6/README.rst +new file mode 100644 +index 0000000..58199dd +--- /dev/null ++++ b/examples/gherkin_v6/README.rst +@@ -0,0 +1,18 @@ ++Gherkin v6 Examples ++============================================================================= ++ ++ ++SCRATCHPAD: Problems ++----------------------------------------------------------------------------- ++ ++- SummaryReporter: Shows wrong counts when Rules are present:: ++ ++ ... ++ 0 features passed, 0 failed, 1 skipped XXX ++ 3 rules passed, 0 failed, 0 skipped ++ 5 scenarios passed, 0 failed, 0 skipped ++ 13 steps passed, 0 failed, 0 skipped, 0 undefined ++ ++ ++- Formatters: PrettyFormatter, PlainFormatter (at least) need Rule support ++ +diff --git a/examples/gherkin_v6/behave.ini b/examples/gherkin_v6/behave.ini +new file mode 100644 +index 0000000..45c0f0d +--- /dev/null ++++ b/examples/gherkin_v6/behave.ini +@@ -0,0 +1,42 @@ ++# ============================================================================= ++# BEHAVE CONFIGURATION ++# ============================================================================= ++# FILE: .behaverc, behave.ini, setup.cfg, tox.ini ++# ++# SEE ALSO: ++# * http://packages.python.org/behave/behave.html#configuration-files ++# * https://github.com/behave/behave ++# * http://pypi.python.org/pypi/behave/ ++# ============================================================================= ++ ++[behave] ++default_tags = not (@xfail or @not_implemented) ++show_skipped = false ++format = rerun ++ progress3 ++outfiles = rerun.txt ++ reports/report_progress3.txt ++junit = true ++logging_level = INFO ++# logging_format = LOG.%(levelname)-8s %(name)-10s: %(message)s ++# logging_format = LOG.%(levelname)-8s %(asctime)s %(name)-10s: %(message)s ++ ++# -- ALLURE-FORMATTER REQUIRES: ++# brew install allure ++# pip install allure-behave ++# ALLURE_REPORTS_DIR=allure.reports ++# behave -f allure -o $ALLURE_REPORTS_DIR ... ++# allure serve $ALLURE_REPORTS_DIR ++# ++# SEE ALSO: ++# * https://github.com/allure-framework/allure2 ++# * https://github.com/allure-framework/allure-python ++[behave.formatters] ++allure = allure_behave.formatter:AllureFormatter ++ ++# PREPARED: ++# [behave] ++# format = ... missing_steps ... ++# output = ... features/steps/missing_steps.py ... ++# [behave.formatters] ++# missing_steps = behave.contrib.formatter_missing_steps:MissingStepsFormatter +diff --git a/examples/gherkin_v6/features/rule_1.feature b/examples/gherkin_v6/features/rule_1.feature +new file mode 100644 +index 0000000..a802e19 +--- /dev/null ++++ b/examples/gherkin_v6/features/rule_1.feature +@@ -0,0 +1,42 @@ ++Feature: Gherkin v6 Example -- with Rules ++ Feature description line 1. ++ ++ Background: Feature.Background ++ Given feature background step_1 ++ ++ Rule: R1 (with Rule.Background) ++ Rule R1 description line 1. ++ ++ Background: R1.Background ++ Given rule R1 background step_1 ++ When rule R1 background step_2 ++ ++ Example: R1.Scenario_1 ++ When rule R1 scenario_1 step_1 ++ Then rule R1 scenario_1 step_2 ++ ++ Example: R1.Scenario_2 ++ Given rule R1 scenario_2 step_1 ++ Then rule R1 scenario_2 step_2 ++ ++ Rule: R2 (without Rule.Background) ++ Rule R2 description line 1. ++ ++ Example: R2.Scenario_1 ++ When rule R2 scenario_1 step_1 ++ Then rule R2 scenario_1 step_2 ++ ++ ++ Rule: R3 (with empty Rule.Background) ++ Rule R3 description line 1. ++ Rule R3 description line 2. ++ ++ Background: R3.EmptyBackground ++ ++ Scenario Template: R3.Scenario ++ Given a person named "" ++ ++ Examples: ++ | name | ++ | Alice | ++ | Bob | +diff --git a/examples/gherkin_v6/features/rule_2.feature b/examples/gherkin_v6/features/rule_2.feature +new file mode 100644 +index 0000000..a802e19 +--- /dev/null ++++ b/examples/gherkin_v6/features/rule_2.feature +@@ -0,0 +1,42 @@ ++Feature: Gherkin v6 Example -- with Rules ++ Feature description line 1. ++ ++ Background: Feature.Background ++ Given feature background step_1 ++ ++ Rule: R1 (with Rule.Background) ++ Rule R1 description line 1. ++ ++ Background: R1.Background ++ Given rule R1 background step_1 ++ When rule R1 background step_2 ++ ++ Example: R1.Scenario_1 ++ When rule R1 scenario_1 step_1 ++ Then rule R1 scenario_1 step_2 ++ ++ Example: R1.Scenario_2 ++ Given rule R1 scenario_2 step_1 ++ Then rule R1 scenario_2 step_2 ++ ++ Rule: R2 (without Rule.Background) ++ Rule R2 description line 1. ++ ++ Example: R2.Scenario_1 ++ When rule R2 scenario_1 step_1 ++ Then rule R2 scenario_1 step_2 ++ ++ ++ Rule: R3 (with empty Rule.Background) ++ Rule R3 description line 1. ++ Rule R3 description line 2. ++ ++ Background: R3.EmptyBackground ++ ++ Scenario Template: R3.Scenario ++ Given a person named "" ++ ++ Examples: ++ | name | ++ | Alice | ++ | Bob | +diff --git a/examples/gherkin_v6/features/steps/example_steps.py b/examples/gherkin_v6/features/steps/example_steps.py +new file mode 100644 +index 0000000..f4822f3 +--- /dev/null ++++ b/examples/gherkin_v6/features/steps/example_steps.py +@@ -0,0 +1,21 @@ ++# -*- coding: UTF-8 -*- ++from __future__ import absolute_import, print_function ++from behave import step ++ ++ ++@step(u'feature background step_{step_id:d}') ++def step_rule_background(ctx, step_id): ++ print("feature background step_{0}".format(step_id)) ++ ++ ++@step(u'rule {rule_id:w} background step_{step_id:d}') ++def step_rule_background(ctx, rule_id, step_id): ++ print("rule {0} background step_{1}".format(rule_id, step_id)) ++ ++ ++@step(u'rule {rule_id:w} scenario_{scenario_id:d} step_{step_id:d}') ++def step_rule_scenario(ctx, rule_id, scenario_id, step_id): ++ print("rule {0} scenario_{1} step_{2}".format( ++ rule_id, scenario_id, step_id)) ++ ++ +diff --git a/examples/gherkin_v6/features/steps/person_steps.py b/examples/gherkin_v6/features/steps/person_steps.py +new file mode 100644 +index 0000000..714ac01 +--- /dev/null ++++ b/examples/gherkin_v6/features/steps/person_steps.py +@@ -0,0 +1,7 @@ ++# -*- coding: UTF-8 -*- ++from behave import given ++ ++ ++@given(u'a person named "{name}"') ++def step_given_person_with_name(ctx, name): ++ pass diff --git a/meta-python/recipes-devtools/python/python3-behave/0017-PREPARE-Python-3.8-support.patch b/meta-python/recipes-devtools/python/python3-behave/0017-PREPARE-Python-3.8-support.patch new file mode 100644 index 000000000..7c6e9b521 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0017-PREPARE-Python-3.8-support.patch @@ -0,0 +1,39 @@ +From 5e529b9ae17c15231f989c17fe1a09897edf6477 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 8 Jun 2019 20:39:42 +0200 +Subject: [PATCH] PREPARE: Python-3.8 support + +--- + .travis.yml | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 7015b88..d8f2443 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -1,12 +1,14 @@ + language: python + sudo: false ++dist: xenial # required for Python >= 3.7 + python: +- - "3.6" ++ - "3.7" + - "2.7" ++ - "3.6" + - "3.5" + - "pypy" + - "pypy3" +- - "3.7-dev" ++ - "3.8-dev" + + # -- DISABLED: + # - "nightly" +@@ -19,7 +21,7 @@ python: + # -- TEST-BALLON: Check if Python 3.6 is actually Python 3.5.1 or newer + matrix: + allow_failures: +- - python: "3.7-dev" ++ - python: "3.8-dev" + - python: "nightly" + + cache: diff --git a/meta-python/recipes-devtools/python/python3-behave/0018-py3.8-Try-to-fix-logging.Formatter-validate-problem.patch b/meta-python/recipes-devtools/python/python3-behave/0018-py3.8-Try-to-fix-logging.Formatter-validate-problem.patch new file mode 100644 index 000000000..4fe596a2f --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0018-py3.8-Try-to-fix-logging.Formatter-validate-problem.patch @@ -0,0 +1,22 @@ +From c046c3a31322c7c29ff6d7f6172df41da3530683 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 13:17:10 +0200 +Subject: [PATCH] py3.8: Try to fix logging.Formatter validate problem + +--- + tests/unit/test_capture.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tests/unit/test_capture.py b/tests/unit/test_capture.py +index ac2655e..d9a3f3a 100644 +--- a/tests/unit/test_capture.py ++++ b/tests/unit/test_capture.py +@@ -20,6 +20,8 @@ def create_capture_controller(config=None): + config.log_capture = True + config.logging_filter = None + config.logging_level = "INFO" ++ config.logging_format = "%(levelname)s:%(name)s:%(message)s" ++ config.logging_datefmt = None + return CaptureController(config) + + def setup_capture_controller(capture_controller, context=None): diff --git a/meta-python/recipes-devtools/python/python3-behave/0019-travis.ci-Temporarily-move-py38-dev-to-front-build-f.patch b/meta-python/recipes-devtools/python/python3-behave/0019-travis.ci-Temporarily-move-py38-dev-to-front-build-f.patch new file mode 100644 index 000000000..121da7094 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0019-travis.ci-Temporarily-move-py38-dev-to-front-build-f.patch @@ -0,0 +1,28 @@ +From fbe6b2937087db11f96e21b36a540270d7c2b165 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 13:19:58 +0200 +Subject: [PATCH] travis.ci: Temporarily move py38-dev to front (build first). + +--- + .travis.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.travis.yml b/.travis.yml +index d8f2443..35bce8c 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -2,13 +2,13 @@ language: python + sudo: false + dist: xenial # required for Python >= 3.7 + python: ++ - "3.8-dev" + - "3.7" + - "2.7" + - "3.6" + - "3.5" + - "pypy" + - "pypy3" +- - "3.8-dev" + + # -- DISABLED: + # - "nightly" diff --git a/meta-python/recipes-devtools/python/python3-behave/0020-travis.ci-Tweaks-for-faster-builds-temporarily.patch b/meta-python/recipes-devtools/python/python3-behave/0020-travis.ci-Tweaks-for-faster-builds-temporarily.patch new file mode 100644 index 000000000..cf9aff701 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0020-travis.ci-Tweaks-for-faster-builds-temporarily.patch @@ -0,0 +1,35 @@ +From 41525c748413405d0faf6c8fc9a345047e31a1a7 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 13:26:19 +0200 +Subject: [PATCH] travis.ci: Tweaks for faster builds (temporarily). + +--- + .travis.yml | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 35bce8c..fbc3520 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -5,15 +5,15 @@ python: + - "3.8-dev" + - "3.7" + - "2.7" +- - "3.6" +- - "3.5" +- - "pypy" +- - "pypy3" ++ ++# -- DISABLE-TEMPORARILY: Ensure faster builds ++# - "3.6" ++# - "3.5" ++# - "pypy" ++# - "pypy3" + + # -- DISABLED: + # - "nightly" +-# - "3.4" +-# - "3.3" + # + # NOW SUPPORTED: "3.5" => python 3.5.2 (>= 3.5.1) + # NOTE: nightly = 3.7-dev diff --git a/meta-python/recipes-devtools/python/python3-behave/0021-FIX-py3.8-logging.Formatter.validate-problem.patch b/meta-python/recipes-devtools/python/python3-behave/0021-FIX-py3.8-logging.Formatter.validate-problem.patch new file mode 100644 index 000000000..5fb1176ac --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0021-FIX-py3.8-logging.Formatter.validate-problem.patch @@ -0,0 +1,47 @@ +From fed4bb3273633e5f81fc8ba21c8b62255c7eefcd Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 13:42:20 +0200 +Subject: [PATCH] FIX py3.8: logging.Formatter.validate() problem. + +--- + test/test_runner.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/test/test_runner.py b/test/test_runner.py +index 57c9445..6647283 100644 +--- a/test/test_runner.py ++++ b/test/test_runner.py +@@ -286,6 +286,7 @@ class TestContext(unittest.TestCase): + eq_("thing" in self.context, True) + del self.context.thing + ++ + class ExampleSteps(object): + text = None + table = None +@@ -320,6 +321,7 @@ class ExampleSteps(object): + for keyword, pattern, func in step_definitions: + step_registry.add_step_definition(keyword, pattern, func) + ++ + class TestContext_ExecuteSteps(unittest.TestCase): + """ + Test the behave.runner.Context.execute_steps() functionality. +@@ -341,6 +343,8 @@ class TestContext_ExecuteSteps(unittest.TestCase): + runner_.config.stdout_capture = False + runner_.config.stderr_capture = False + runner_.config.log_capture = False ++ runner_.config.logging_format = None ++ runner_.config.logging_datefmt = None + runner_.step_registry = self.step_registry + + self.context = runner.Context(runner_) +@@ -658,6 +662,8 @@ class TestRunWithPaths(unittest.TestCase): + self.config.logging_filter = None + self.config.outputs = [Mock(), StreamOpener(stream=sys.stdout)] + self.config.format = ["plain", "progress"] ++ self.config.logging_format = None ++ self.config.logging_datefmt = None + self.runner = runner.Runner(self.config) + self.load_hooks = self.runner.load_hooks = Mock() + self.load_step_definitions = self.runner.load_step_definitions = Mock() diff --git a/meta-python/recipes-devtools/python/python3-behave/0022-PREPARE-FOR-Python-3.8-asyncio.coroutine-is-deprecat.patch b/meta-python/recipes-devtools/python/python3-behave/0022-PREPARE-FOR-Python-3.8-asyncio.coroutine-is-deprecat.patch new file mode 100644 index 000000000..b46846478 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0022-PREPARE-FOR-Python-3.8-asyncio.coroutine-is-deprecat.patch @@ -0,0 +1,42 @@ +From d54d8c038a7e5f9d2168daa6e13362d1df7d17a5 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 13:58:22 +0200 +Subject: [PATCH] PREPARE FOR: Python 3.8, @asyncio.coroutine is deprecated + since py38. + +--- + tests/api/_test_async_step34.py | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/tests/api/_test_async_step34.py b/tests/api/_test_async_step34.py +index 8242be7..1c0c31f 100644 +--- a/tests/api/_test_async_step34.py ++++ b/tests/api/_test_async_step34.py +@@ -37,13 +37,16 @@ from .testing_support_async import AsyncStepTheory + # ----------------------------------------------------------------------------- + # TEST MARKERS: + # ----------------------------------------------------------------------------- ++# DEPRECATED: @asyncio.coroutine decorator (since: Python >= 3.8) + _python_version = float("%s.%s" % sys.version_info[:2]) +-py34_or_newer = pytest.mark.skipif(_python_version < 3.4, reason="Needs Python >= 3.4") ++requires_py34_to_py37 = pytest.mark.skipif(not (3.4 <= _python_version < 3.8), ++ reason="Supported only for python.versions: 3.4 .. 3.7 (inclusive)") ++ + + # ----------------------------------------------------------------------------- + # TESTSUITE: + # ----------------------------------------------------------------------------- +-@py34_or_newer ++@requires_py34_to_py37 + class TestAsyncStepDecoratorPy34(object): + + def test_step_decorator_async_run_until_complete2(self): +@@ -128,7 +131,7 @@ class TestAsyncContext(object): + assert async_context.loop is loop0 + + +-@py34_or_newer ++@requires_py34_to_py37 + class TestAsyncStepRunPy34(object): + """Ensure that execution of async-steps works as expected.""" + diff --git a/meta-python/recipes-devtools/python/python3-behave/0023-UPDATE-Add-755-info.patch b/meta-python/recipes-devtools/python/python3-behave/0023-UPDATE-Add-755-info.patch new file mode 100644 index 000000000..56b8bd6c7 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0023-UPDATE-Add-755-info.patch @@ -0,0 +1,24 @@ +From db66eecf4c5301ca45db9f71c7b2c3c26d5927a1 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 14:06:14 +0200 +Subject: [PATCH] UPDATE: Add #755 info + +--- + CHANGES.rst | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/CHANGES.rst b/CHANGES.rst +index d6e96af..a91e22a 100644 +--- a/CHANGES.rst ++++ b/CHANGES.rst +@@ -30,6 +30,10 @@ ENHANCEMENTS: + * issue #675: Feature files cannot be found within symlink directories (provided by: smadness, pull #680) + + ++PARTIALLY FIXED: ++ ++* issue #755: Failures with Python 3.8 (submitted by: hroncok) ++ + FIXED: + + * issue #725: Scenario Outline description lines seem to be ignored (submitted by: nizwiz) diff --git a/meta-python/recipes-devtools/python/python3-behave/0024-FIX-WARNING-Related-to-docstring-example-and-weird-b.patch b/meta-python/recipes-devtools/python/python3-behave/0024-FIX-WARNING-Related-to-docstring-example-and-weird-b.patch new file mode 100644 index 000000000..8f9464a2d --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0024-FIX-WARNING-Related-to-docstring-example-and-weird-b.patch @@ -0,0 +1,25 @@ +From 467b223dc84f52933b2782ce9d116e89d48a26f1 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 14:27:23 +0200 +Subject: [PATCH] FIX-WARNING: Related to docstring-example and weird backslash + usage. + +--- + behave/matchers.py | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/behave/matchers.py b/behave/matchers.py +index c896f52..0fee0c7 100644 +--- a/behave/matchers.py ++++ b/behave/matchers.py +@@ -261,9 +261,7 @@ class CFParseMatcher(ParseMatcher): + + + def register_type(**kw): +- # pylint: disable=anomalous-backslash-in-string +- # REQUIRED-BY: code example +- """Registers a custom type that will be available to "parse" ++ r"""Registers a custom type that will be available to "parse" + for type conversion during step matching. + + Converters should be supplied as ``name=callable`` arguments (or as dict). diff --git a/meta-python/recipes-devtools/python/python3-behave/0025-FIX-invalid-escape-sequence-warnings-w-regex-pattern.patch b/meta-python/recipes-devtools/python/python3-behave/0025-FIX-invalid-escape-sequence-warnings-w-regex-pattern.patch new file mode 100644 index 000000000..9213ccd2e --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0025-FIX-invalid-escape-sequence-warnings-w-regex-pattern.patch @@ -0,0 +1,29 @@ +From 11ea2c45d5b88c92b25ab8e8027a931df81c2abc Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 14:37:41 +0200 +Subject: [PATCH] FIX: invalid escape sequence warnings (w/ regex patterns). + +--- + tests/unit/test_behave4cmd_command_shell_proc.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/unit/test_behave4cmd_command_shell_proc.py b/tests/unit/test_behave4cmd_command_shell_proc.py +index aae5e9f..c45ab3b 100644 +--- a/tests/unit/test_behave4cmd_command_shell_proc.py ++++ b/tests/unit/test_behave4cmd_command_shell_proc.py +@@ -1,5 +1,5 @@ + # -*- coding: UTF-8 -*- +-""" ++r""" + + Regular expressions for winpath: + http://regexlib.com/Search.aspx?k=file+name +@@ -61,7 +61,7 @@ line_processor_ioerrors = [ + + line_processor_traceback = [ + ExceptionWithPathNormalizer( +- '^\s*File "(?P.*)", line \d+, in ', ++ r'^\s*File "(?P.*)", line \d+, in ', + ' File "'), + BehaveWinCommandOutputProcessor.line_processors[4], + ] diff --git a/meta-python/recipes-devtools/python/python3-behave/0026-DEPRECATING-CLEANUP-Move-deprecated-tag-matcher-clas.patch b/meta-python/recipes-devtools/python/python3-behave/0026-DEPRECATING-CLEANUP-Move-deprecated-tag-matcher-clas.patch new file mode 100644 index 000000000..67ea8860e --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0026-DEPRECATING-CLEANUP-Move-deprecated-tag-matcher-clas.patch @@ -0,0 +1,1020 @@ +From 72279d87372cf21d980c40d01fce2f59bb734884 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 16:04:43 +0200 +Subject: [PATCH] DEPRECATING-CLEANUP: Move deprecated tag matcher classes + +- behave.tag_matcher.OnlyWithCategoryTagMatcher +- behave.tag_matcher.OnlyWithAnyCategoryTagMatcher + +to "behave.attic.tag_matcher". +Move related unit tests to "tests.attic/unit/test_tag_matcher.py". +--- + behave/attic/__init__.py | 0 + behave/attic/tag_matcher.py | 181 +++++++++++++++++ + behave/tag_matcher.py | 189 +----------------- + tests.attic/__init__.py | 0 + tests.attic/unit/__init__.py | 0 + tests.attic/unit/test_tag_matcher.py | 280 +++++++++++++++++++++++++++ + tests/unit/test_tag_matcher.py | 279 +------------------------- + 7 files changed, 470 insertions(+), 459 deletions(-) + create mode 100644 behave/attic/__init__.py + create mode 100644 behave/attic/tag_matcher.py + create mode 100644 tests.attic/__init__.py + create mode 100644 tests.attic/unit/__init__.py + create mode 100644 tests.attic/unit/test_tag_matcher.py + +diff --git a/behave/attic/__init__.py b/behave/attic/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/behave/attic/tag_matcher.py b/behave/attic/tag_matcher.py +new file mode 100644 +index 0000000..f07dcbf +--- /dev/null ++++ b/behave/attic/tag_matcher.py +@@ -0,0 +1,181 @@ ++# ----------------------------------------------------------------------------- ++# PROTOTYPING CLASSES: Should no longer be used ++# ----------------------------------------------------------------------------- ++ ++import warnings ++from behave.tag_matcher import TagMatcher ++ ++ ++class OnlyWithCategoryTagMatcher(TagMatcher): ++ """ ++ Provides a tag matcher that allows to determine if feature/scenario ++ should run or should be excluded from the run-set (at runtime). ++ ++ .. deprecated:: Use :class:`ActiveTagMatcher` instead. ++ ++ EXAMPLE: ++ -------- ++ ++ Run some scenarios only when runtime conditions are met: ++ ++ * Run scenario Alice only on Windows OS ++ * Run scenario Bob only on MACOSX ++ ++ .. code-block:: gherkin ++ ++ # -- FILE: features/alice.feature ++ # TAG SCHEMA: @only.with_{category}={current_value} ++ Feature: ++ ++ @only.with_os=win32 ++ Scenario: Alice (Run only on Windows) ++ Given I do something ++ ... ++ ++ @only.with_os=darwin ++ Scenario: Bob (Run only on MACOSX) ++ Given I do something else ++ ... ++ ++ ++ .. code-block:: python ++ ++ # -- FILE: features/environment.py ++ from behave.tag_matcher import OnlyWithCategoryTagMatcher ++ import sys ++ ++ # -- MATCHES TAGS: @only.with_{category}=* = @only.with_os=* ++ active_tag_matcher = OnlyWithCategoryTagMatcher("os", sys.platform) ++ ++ def before_scenario(context, scenario): ++ if active_tag_matcher.should_exclude_with(scenario.effective_tags): ++ scenario.skip() #< LATE-EXCLUDE from run-set. ++ """ ++ tag_prefix = "only.with_" ++ value_separator = "=" ++ ++ def __init__(self, category, value, tag_prefix=None, value_sep=None): ++ warnings.warn("Use ActiveTagMatcher instead.", DeprecationWarning) ++ super(OnlyWithCategoryTagMatcher, self).__init__() ++ self.active_tag = self.make_category_tag(category, value, ++ tag_prefix, value_sep) ++ self.category_tag_prefix = self.make_category_tag(category, None, ++ tag_prefix, value_sep) ++ ++ def should_exclude_with(self, tags): ++ category_tags = self.select_category_tags(tags) ++ if category_tags and self.active_tag not in category_tags: ++ return True ++ # -- OTHERWISE: feature/scenario with theses tags should run. ++ return False ++ ++ def select_category_tags(self, tags): ++ return [tag for tag in tags ++ if tag.startswith(self.category_tag_prefix)] ++ ++ @classmethod ++ def make_category_tag(cls, category, value=None, tag_prefix=None, ++ value_sep=None): ++ if tag_prefix is None: ++ tag_prefix = cls.tag_prefix ++ if value_sep is None: ++ value_sep = cls.value_separator ++ value = value or "" ++ return "%s%s%s%s" % (tag_prefix, category, value_sep, value) ++ ++ ++class OnlyWithAnyCategoryTagMatcher(TagMatcher): ++ """ ++ Provides a tag matcher that matches any category that follows the ++ "@only.with_" tag schema and determines if it should run or ++ should be excluded from the run-set (at runtime). ++ ++ TAG SCHEMA: @only.with_{category}={value} ++ ++ .. seealso:: OnlyWithCategoryTagMatcher ++ .. deprecated:: Use :class:`ActiveTagMatcher` instead. ++ ++ EXAMPLE: ++ -------- ++ ++ Run some scenarios only when runtime conditions are met: ++ ++ * Run scenario Alice only on Windows OS ++ * Run scenario Bob only with browser Chrome ++ ++ .. code-block:: gherkin ++ ++ # -- FILE: features/alice.feature ++ # TAG SCHEMA: @only.with_{category}={current_value} ++ Feature: ++ ++ @only.with_os=win32 ++ Scenario: Alice (Run only on Windows) ++ Given I do something ++ ... ++ ++ @only.with_browser=chrome ++ Scenario: Bob (Run only with Web-Browser Chrome) ++ Given I do something else ++ ... ++ ++ ++ .. code-block:: python ++ ++ # -- FILE: features/environment.py ++ from behave.tag_matcher import OnlyWithAnyCategoryTagMatcher ++ import sys ++ ++ # -- MATCHES ANY TAGS: @only.with_{category}={value} ++ # NOTE: active_tag_value_provider provides current category values. ++ active_tag_value_provider = { ++ "browser": os.environ.get("BEHAVE_BROWSER", "chrome"), ++ "os": sys.platform, ++ } ++ active_tag_matcher = OnlyWithAnyCategoryTagMatcher(active_tag_value_provider) ++ ++ def before_scenario(context, scenario): ++ if active_tag_matcher.should_exclude_with(scenario.effective_tags): ++ scenario.skip() #< LATE-EXCLUDE from run-set. ++ """ ++ ++ def __init__(self, value_provider, tag_prefix=None, value_sep=None): ++ warnings.warn("Use ActiveTagMatcher instead.", DeprecationWarning) ++ super(OnlyWithAnyCategoryTagMatcher, self).__init__() ++ if value_sep is None: ++ value_sep = OnlyWithCategoryTagMatcher.value_separator ++ self.value_provider = value_provider ++ self.tag_prefix = tag_prefix or OnlyWithCategoryTagMatcher.tag_prefix ++ self.value_separator = value_sep ++ ++ def should_exclude_with(self, tags): ++ exclude_decision_map = {} ++ for category_tag in self.select_category_tags(tags): ++ category, value = self.parse_category_tag(category_tag) ++ active_value = self.value_provider.get(category, None) ++ if active_value is None: ++ # -- CASE: Unknown category, ignore it. ++ continue ++ elif active_value == value: ++ # -- CASE: Active category value selected, decision should run. ++ exclude_decision_map[category] = False ++ else: ++ # -- CASE: Inactive category value selected, may exclude it. ++ if category not in exclude_decision_map: ++ exclude_decision_map[category] = True ++ return any(exclude_decision_map.values()) ++ ++ def select_category_tags(self, tags): ++ return [tag for tag in tags ++ if tag.startswith(self.tag_prefix)] ++ ++ def parse_category_tag(self, tag): ++ assert tag and tag.startswith(self.tag_prefix) ++ category_value = tag[len(self.tag_prefix):] ++ if self.value_separator in category_value: ++ category, value = category_value.split(self.value_separator, 1) ++ else: ++ # -- OOPS: TAG SCHEMA FORMAT MISMATCH ++ category = category_value ++ value = None ++ return category, value +diff --git a/behave/tag_matcher.py b/behave/tag_matcher.py +index f1f955b..5f9dce0 100644 +--- a/behave/tag_matcher.py ++++ b/behave/tag_matcher.py +@@ -1,9 +1,12 @@ +-# -*- coding: utf-8 -*- ++# -*- coding: UTF-8 -*- ++""" ++Contains classes and functionality to provide a skip-if logic based on tags ++in feature files. ++""" + + from __future__ import absolute_import + import re + import operator +-import warnings + import six + + +@@ -34,10 +37,10 @@ class ActiveTagMatcher(TagMatcher): + """Provides an active tag matcher for many categories. + + TAG SCHEMA: +- * active.with_{category}={value} +- * not_active.with_{category}={value} + * use.with_{category}={value} + * not.with_{category}={value} ++ * active.with_{category}={value} ++ * not_active.with_{category}={value} + * only.with_{category}={value} (NOTE: For backward compatibility) + + TAG LOGIC +@@ -285,181 +288,3 @@ def setup_active_tag_values(active_tag_values, data): + for category in list(active_tag_values.keys()): + if category in data: + active_tag_values[category] = data[category] +- +- +-# ----------------------------------------------------------------------------- +-# PROTOTYPING CLASSES: +-# ----------------------------------------------------------------------------- +-class OnlyWithCategoryTagMatcher(TagMatcher): +- """ +- Provides a tag matcher that allows to determine if feature/scenario +- should run or should be excluded from the run-set (at runtime). +- +- .. deprecated:: Use :class:`ActiveTagMatcher` instead. +- +- EXAMPLE: +- -------- +- +- Run some scenarios only when runtime conditions are met: +- +- * Run scenario Alice only on Windows OS +- * Run scenario Bob only on MACOSX +- +- .. code-block:: gherkin +- +- # -- FILE: features/alice.feature +- # TAG SCHEMA: @only.with_{category}={current_value} +- Feature: +- +- @only.with_os=win32 +- Scenario: Alice (Run only on Windows) +- Given I do something +- ... +- +- @only.with_os=darwin +- Scenario: Bob (Run only on MACOSX) +- Given I do something else +- ... +- +- +- .. code-block:: python +- +- # -- FILE: features/environment.py +- from behave.tag_matcher import OnlyWithCategoryTagMatcher +- import sys +- +- # -- MATCHES TAGS: @only.with_{category}=* = @only.with_os=* +- active_tag_matcher = OnlyWithCategoryTagMatcher("os", sys.platform) +- +- def before_scenario(context, scenario): +- if active_tag_matcher.should_exclude_with(scenario.effective_tags): +- scenario.skip() #< LATE-EXCLUDE from run-set. +- """ +- tag_prefix = "only.with_" +- value_separator = "=" +- +- def __init__(self, category, value, tag_prefix=None, value_sep=None): +- warnings.warn("Use ActiveTagMatcher instead.", DeprecationWarning) +- super(OnlyWithCategoryTagMatcher, self).__init__() +- self.active_tag = self.make_category_tag(category, value, +- tag_prefix, value_sep) +- self.category_tag_prefix = self.make_category_tag(category, None, +- tag_prefix, value_sep) +- +- def should_exclude_with(self, tags): +- category_tags = self.select_category_tags(tags) +- if category_tags and self.active_tag not in category_tags: +- return True +- # -- OTHERWISE: feature/scenario with theses tags should run. +- return False +- +- def select_category_tags(self, tags): +- return [tag for tag in tags +- if tag.startswith(self.category_tag_prefix)] +- +- @classmethod +- def make_category_tag(cls, category, value=None, tag_prefix=None, +- value_sep=None): +- if tag_prefix is None: +- tag_prefix = cls.tag_prefix +- if value_sep is None: +- value_sep = cls.value_separator +- value = value or "" +- return "%s%s%s%s" % (tag_prefix, category, value_sep, value) +- +- +-class OnlyWithAnyCategoryTagMatcher(TagMatcher): +- """ +- Provides a tag matcher that matches any category that follows the +- "@only.with_" tag schema and determines if it should run or +- should be excluded from the run-set (at runtime). +- +- TAG SCHEMA: @only.with_{category}={value} +- +- .. seealso:: OnlyWithCategoryTagMatcher +- .. deprecated:: Use :class:`ActiveTagMatcher` instead. +- +- EXAMPLE: +- -------- +- +- Run some scenarios only when runtime conditions are met: +- +- * Run scenario Alice only on Windows OS +- * Run scenario Bob only with browser Chrome +- +- .. code-block:: gherkin +- +- # -- FILE: features/alice.feature +- # TAG SCHEMA: @only.with_{category}={current_value} +- Feature: +- +- @only.with_os=win32 +- Scenario: Alice (Run only on Windows) +- Given I do something +- ... +- +- @only.with_browser=chrome +- Scenario: Bob (Run only with Web-Browser Chrome) +- Given I do something else +- ... +- +- +- .. code-block:: python +- +- # -- FILE: features/environment.py +- from behave.tag_matcher import OnlyWithAnyCategoryTagMatcher +- import sys +- +- # -- MATCHES ANY TAGS: @only.with_{category}={value} +- # NOTE: active_tag_value_provider provides current category values. +- active_tag_value_provider = { +- "browser": os.environ.get("BEHAVE_BROWSER", "chrome"), +- "os": sys.platform, +- } +- active_tag_matcher = OnlyWithAnyCategoryTagMatcher(active_tag_value_provider) +- +- def before_scenario(context, scenario): +- if active_tag_matcher.should_exclude_with(scenario.effective_tags): +- scenario.skip() #< LATE-EXCLUDE from run-set. +- """ +- +- def __init__(self, value_provider, tag_prefix=None, value_sep=None): +- warnings.warn("Use ActiveTagMatcher instead.", DeprecationWarning) +- super(OnlyWithAnyCategoryTagMatcher, self).__init__() +- if value_sep is None: +- value_sep = OnlyWithCategoryTagMatcher.value_separator +- self.value_provider = value_provider +- self.tag_prefix = tag_prefix or OnlyWithCategoryTagMatcher.tag_prefix +- self.value_separator = value_sep +- +- def should_exclude_with(self, tags): +- exclude_decision_map = {} +- for category_tag in self.select_category_tags(tags): +- category, value = self.parse_category_tag(category_tag) +- active_value = self.value_provider.get(category, None) +- if active_value is None: +- # -- CASE: Unknown category, ignore it. +- continue +- elif active_value == value: +- # -- CASE: Active category value selected, decision should run. +- exclude_decision_map[category] = False +- else: +- # -- CASE: Inactive category value selected, may exclude it. +- if category not in exclude_decision_map: +- exclude_decision_map[category] = True +- return any(exclude_decision_map.values()) +- +- def select_category_tags(self, tags): +- return [tag for tag in tags +- if tag.startswith(self.tag_prefix)] +- +- def parse_category_tag(self, tag): +- assert tag and tag.startswith(self.tag_prefix) +- category_value = tag[len(self.tag_prefix):] +- if self.value_separator in category_value: +- category, value = category_value.split(self.value_separator, 1) +- else: +- # -- OOPS: TAG SCHEMA FORMAT MISMATCH +- category = category_value +- value = None +- return category, value +diff --git a/tests.attic/__init__.py b/tests.attic/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/tests.attic/unit/__init__.py b/tests.attic/unit/__init__.py +new file mode 100644 +index 0000000..e69de29 +diff --git a/tests.attic/unit/test_tag_matcher.py b/tests.attic/unit/test_tag_matcher.py +new file mode 100644 +index 0000000..d767fa7 +--- /dev/null ++++ b/tests.attic/unit/test_tag_matcher.py +@@ -0,0 +1,280 @@ ++# ----------------------------------------------------------------------------- ++# PROTOTYPING CLASSES (deprecating) -- Should no longer be used. ++# ----------------------------------------------------------------------------- ++ ++import warnings ++from unittest import TestCase ++from behave.attic.tag_matcher import \ ++ OnlyWithCategoryTagMatcher, OnlyWithAnyCategoryTagMatcher ++ ++ ++class TestOnlyWithCategoryTagMatcher(TestCase): ++ TagMatcher = OnlyWithCategoryTagMatcher ++ ++ def setUp(self): ++ category = "xxx" ++ with warnings.catch_warnings(): ++ warnings.simplefilter("ignore", DeprecationWarning) ++ self.tag_matcher = OnlyWithCategoryTagMatcher(category, "alice") ++ self.enabled_tag = self.TagMatcher.make_category_tag(category, "alice") ++ self.similar_tag = self.TagMatcher.make_category_tag(category, "alice2") ++ self.other_tag = self.TagMatcher.make_category_tag(category, "other") ++ self.category = category ++ ++ def test_should_exclude_with__returns_false_with_enabled_tag(self): ++ tags = [ self.enabled_tag ] ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags)) ++ ++ def test_should_exclude_with__returns_false_with_enabled_tag_and_more(self): ++ test_patterns = [ ++ ([ self.enabled_tag, self.other_tag ], "case: first"), ++ ([ self.other_tag, self.enabled_tag ], "case: last"), ++ ([ "foo", self.enabled_tag, self.other_tag, "bar" ], "case: middle"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_true_with_other_tag(self): ++ tags = [ self.other_tag ] ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) ++ ++ def test_should_exclude_with__returns_true_with_other_tag_and_more(self): ++ test_patterns = [ ++ ([ self.other_tag, "foo" ], "case: first"), ++ ([ "foo", self.other_tag ], "case: last"), ++ ([ "foo", self.other_tag, "bar" ], "case: middle"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_true_with_similar_tag(self): ++ tags = [ self.similar_tag ] ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) ++ ++ def test_should_exclude_with__returns_true_with_similar_and_more(self): ++ test_patterns = [ ++ ([ self.similar_tag, "foo" ], "case: first"), ++ ([ "foo", self.similar_tag ], "case: last"), ++ ([ "foo", self.similar_tag, "bar" ], "case: middle"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_false_without_category_tag(self): ++ test_patterns = [ ++ ([ ], "case: No tags"), ++ ([ "foo" ], "case: One tag"), ++ ([ "foo", "bar" ], "case: Two tags"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_run_with__negates_result_of_should_exclude_with(self): ++ test_patterns = [ ++ ([ ], "case: No tags"), ++ ([ "foo" ], "case: One non-category tag"), ++ ([ "foo", "bar" ], "case: Two non-category tags"), ++ ([ self.enabled_tag ], "case: enabled tag"), ++ ([ self.enabled_tag, self.other_tag ], "case: enabled and other tag"), ++ ([ self.enabled_tag, "foo" ], "case: enabled and foo tag"), ++ ([ self.other_tag ], "case: other tag"), ++ ([ self.other_tag, "foo" ], "case: other and foo tag"), ++ ([ self.similar_tag ], "case: similar tag"), ++ ([ "foo", self.similar_tag ], "case: foo and similar tag"), ++ ] ++ for tags, case in test_patterns: ++ result1 = self.tag_matcher.should_run_with(tags) ++ result2 = self.tag_matcher.should_exclude_with(tags) ++ self.assertEqual(result1, not result2, "%s: tags=%s" % (case, tags)) ++ self.assertEqual(not result1, result2, "%s: tags=%s" % (case, tags)) ++ ++ def test_make_category_tag__returns_category_tag_prefix_without_value(self): ++ category = "xxx" ++ tag1 = OnlyWithCategoryTagMatcher.make_category_tag(category) ++ tag2 = OnlyWithCategoryTagMatcher.make_category_tag(category, None) ++ tag3 = OnlyWithCategoryTagMatcher.make_category_tag(category, value=None) ++ self.assertEqual("only.with_xxx=", tag1) ++ self.assertEqual("only.with_xxx=", tag2) ++ self.assertEqual("only.with_xxx=", tag3) ++ self.assertTrue(tag1.startswith(OnlyWithCategoryTagMatcher.tag_prefix)) ++ ++ def test_make_category_tag__returns_category_tag_with_value(self): ++ category = "xxx" ++ tag1 = OnlyWithCategoryTagMatcher.make_category_tag(category, "alice") ++ tag2 = OnlyWithCategoryTagMatcher.make_category_tag(category, "bob") ++ self.assertEqual("only.with_xxx=alice", tag1) ++ self.assertEqual("only.with_xxx=bob", tag2) ++ ++ def test_make_category_tag__returns_category_tag_with_tag_prefix(self): ++ my_tag_prefix = "ONLY_WITH." ++ category = "xxx" ++ TagMatcher = OnlyWithCategoryTagMatcher ++ tag0 = TagMatcher.make_category_tag(category, tag_prefix=my_tag_prefix) ++ tag1 = TagMatcher.make_category_tag(category, "alice", my_tag_prefix) ++ tag2 = TagMatcher.make_category_tag(category, "bob", tag_prefix=my_tag_prefix) ++ self.assertEqual("ONLY_WITH.xxx=", tag0) ++ self.assertEqual("ONLY_WITH.xxx=alice", tag1) ++ self.assertEqual("ONLY_WITH.xxx=bob", tag2) ++ self.assertTrue(tag1.startswith(my_tag_prefix)) ++ ++ def test_ctor__with_tag_prefix(self): ++ tag_prefix = "ONLY_WITH." ++ tag_matcher = OnlyWithCategoryTagMatcher("xxx", "alice", tag_prefix) ++ ++ tags = ["foo", "ONLY_WITH.xxx=foo", "only.with_xxx=bar", "bar"] ++ actual_tags = tag_matcher.select_category_tags(tags) ++ self.assertEqual(["ONLY_WITH.xxx=foo"], actual_tags) ++ ++ ++class Traits4OnlyWithAnyCategoryTagMatcher(object): ++ """Test data for OnlyWithAnyCategoryTagMatcher.""" ++ ++ TagMatcher0 = OnlyWithCategoryTagMatcher ++ TagMatcher = OnlyWithAnyCategoryTagMatcher ++ category1_enabled_tag = TagMatcher0.make_category_tag("foo", "alice") ++ category1_similar_tag = TagMatcher0.make_category_tag("foo", "alice2") ++ category1_disabled_tag = TagMatcher0.make_category_tag("foo", "bob") ++ category2_enabled_tag = TagMatcher0.make_category_tag("bar", "BOB") ++ category2_similar_tag = TagMatcher0.make_category_tag("bar", "BOB2") ++ category2_disabled_tag = TagMatcher0.make_category_tag("bar", "CHARLY") ++ unknown_category_tag = TagMatcher0.make_category_tag("UNKNOWN", "one") ++ ++ ++class TestOnlyWithAnyCategoryTagMatcher(TestCase): ++ TagMatcher = OnlyWithAnyCategoryTagMatcher ++ traits = Traits4OnlyWithAnyCategoryTagMatcher ++ ++ def setUp(self): ++ value_provider = { ++ "foo": "alice", ++ "bar": "BOB", ++ } ++ with warnings.catch_warnings(): ++ warnings.simplefilter("ignore", DeprecationWarning) ++ self.tag_matcher = self.TagMatcher(value_provider) ++ ++ # def test_deprecating_warning_is_issued(self): ++ # value_provider = {"foo": "alice"} ++ # with warnings.catch_warnings(record=True) as recorder: ++ # warnings.simplefilter("always", DeprecationWarning) ++ # tag_matcher = OnlyWithAnyCategoryTagMatcher(value_provider) ++ # self.assertEqual(len(recorder), 1) ++ # last_warning = recorder[-1] ++ # assert issubclass(last_warning.category, DeprecationWarning) ++ # assert "deprecated" in str(last_warning.message) ++ ++ def test_should_exclude_with__returns_false_with_enabled_tag(self): ++ traits = self.traits ++ tags1 = [ traits.category1_enabled_tag ] ++ tags2 = [ traits.category2_enabled_tag ] ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags1)) ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags2)) ++ ++ def test_should_exclude_with__returns_false_with_enabled_tag_and_more(self): ++ traits = self.traits ++ test_patterns = [ ++ ([ traits.category1_enabled_tag, traits.category1_disabled_tag ], "case: first"), ++ ([ traits.category1_disabled_tag, traits.category1_enabled_tag ], "case: last"), ++ ([ "foo", traits.category1_enabled_tag, traits.category1_disabled_tag, "bar" ], "case: middle"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_true_with_other_tag(self): ++ traits = self.traits ++ tags = [ traits.category1_disabled_tag ] ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) ++ ++ def test_should_exclude_with__returns_true_with_other_tag_and_more(self): ++ traits = self.traits ++ test_patterns = [ ++ ([ traits.category1_disabled_tag, "foo" ], "case: first"), ++ ([ "foo", traits.category1_disabled_tag ], "case: last"), ++ ([ "foo", traits.category1_disabled_tag, "bar" ], "case: middle"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_true_with_similar_tag(self): ++ traits = self.traits ++ tags = [ traits.category1_similar_tag ] ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) ++ ++ def test_should_exclude_with__returns_true_with_similar_and_more(self): ++ traits = self.traits ++ test_patterns = [ ++ ([ traits.category1_similar_tag, "foo" ], "case: first"), ++ ([ "foo", traits.category1_similar_tag ], "case: last"), ++ ([ "foo", traits.category1_similar_tag, "bar" ], "case: middle"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_false_without_category_tag(self): ++ test_patterns = [ ++ ([ ], "case: No tags"), ++ ([ "foo" ], "case: One tag"), ++ ([ "foo", "bar" ], "case: Two tags"), ++ ] ++ for tags, case in test_patterns: ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_exclude_with__returns_false_with_unknown_category_tag(self): ++ """Tags from unknown categories, not supported by value_provider, ++ should not be excluded. ++ """ ++ traits = self.traits ++ tags = [ traits.unknown_category_tag ] ++ self.assertEqual("only.with_UNKNOWN=one", traits.unknown_category_tag) ++ self.assertEqual(None, self.tag_matcher.value_provider.get("UNKNOWN")) ++ self.assertEqual(False, self.tag_matcher.should_exclude_with(tags)) ++ ++ def test_should_exclude_with__combinations_of_2_categories(self): ++ traits = self.traits ++ test_patterns = [ ++ ("case 00: 2 disabled category tags", True, ++ [ traits.category1_disabled_tag, traits.category2_disabled_tag]), ++ ("case 01: disabled and enabled category tags", True, ++ [ traits.category1_disabled_tag, traits.category2_enabled_tag]), ++ ("case 10: enabled and disabled category tags", True, ++ [ traits.category1_enabled_tag, traits.category2_disabled_tag]), ++ ("case 11: 2 enabled category tags", False, # -- SHOULD-RUN ++ [ traits.category1_enabled_tag, traits.category2_enabled_tag]), ++ # -- SPECIAL CASE: With unknown category ++ ("case 0x: disabled and unknown category tags", True, ++ [ traits.category1_disabled_tag, traits.unknown_category_tag]), ++ ("case 1x: enabled and unknown category tags", False, # SHOULD-RUN ++ [ traits.category1_enabled_tag, traits.unknown_category_tag]), ++ ] ++ for case, expected, tags in test_patterns: ++ actual_result = self.tag_matcher.should_exclude_with(tags) ++ self.assertEqual(expected, actual_result, ++ "%s: tags=%s" % (case, tags)) ++ ++ def test_should_run_with__negates_result_of_should_exclude_with(self): ++ traits = self.traits ++ test_patterns = [ ++ ([ ], "case: No tags"), ++ ([ "foo" ], "case: One non-category tag"), ++ ([ "foo", "bar" ], "case: Two non-category tags"), ++ ([ traits.category1_enabled_tag ], "case: enabled tag"), ++ ([ traits.category1_enabled_tag, traits.category1_disabled_tag ], "case: enabled and other tag"), ++ ([ traits.category1_enabled_tag, "foo" ], "case: enabled and foo tag"), ++ ([ traits.category1_disabled_tag ], "case: other tag"), ++ ([ traits.category1_disabled_tag, "foo" ], "case: other and foo tag"), ++ ([ traits.category1_similar_tag ], "case: similar tag"), ++ ([ "foo", traits.category1_similar_tag ], "case: foo and similar tag"), ++ ] ++ for tags, case in test_patterns: ++ result1 = self.tag_matcher.should_run_with(tags) ++ result2 = self.tag_matcher.should_exclude_with(tags) ++ self.assertEqual(result1, not result2, "%s: tags=%s" % (case, tags)) ++ self.assertEqual(not result1, result2, "%s: tags=%s" % (case, tags)) +diff --git a/tests/unit/test_tag_matcher.py b/tests/unit/test_tag_matcher.py +index c5d2266..a04c1d4 100644 +--- a/tests/unit/test_tag_matcher.py ++++ b/tests/unit/test_tag_matcher.py +@@ -8,12 +8,13 @@ Unit tests for active tag-matcher (mod:`behave.tag_matcher`). + """ + + from __future__ import absolute_import +-from behave.tag_matcher import * + from mock import Mock + from unittest import TestCase + import warnings + import pytest + ++from behave.tag_matcher import * ++ + + class Traits4ActiveTagMatcher(object): + TagMatcher = ActiveTagMatcher +@@ -460,279 +461,3 @@ class TestCompositeTagMatcher(TestCase): + actual_true_count = self.count_tag_matcher_with_result( + self.ctag_matcher.tag_matchers, tags, True) + self.assertEqual(0, actual_true_count) +- +- +-# ----------------------------------------------------------------------------- +-# PROTOTYPING CLASSES (deprecating) +-# ----------------------------------------------------------------------------- +-class TestOnlyWithCategoryTagMatcher(TestCase): +- TagMatcher = OnlyWithCategoryTagMatcher +- +- def setUp(self): +- category = "xxx" +- with warnings.catch_warnings(): +- warnings.simplefilter("ignore", DeprecationWarning) +- self.tag_matcher = OnlyWithCategoryTagMatcher(category, "alice") +- self.enabled_tag = self.TagMatcher.make_category_tag(category, "alice") +- self.similar_tag = self.TagMatcher.make_category_tag(category, "alice2") +- self.other_tag = self.TagMatcher.make_category_tag(category, "other") +- self.category = category +- +- def test_should_exclude_with__returns_false_with_enabled_tag(self): +- tags = [ self.enabled_tag ] +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags)) +- +- def test_should_exclude_with__returns_false_with_enabled_tag_and_more(self): +- test_patterns = [ +- ([ self.enabled_tag, self.other_tag ], "case: first"), +- ([ self.other_tag, self.enabled_tag ], "case: last"), +- ([ "foo", self.enabled_tag, self.other_tag, "bar" ], "case: middle"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_true_with_other_tag(self): +- tags = [ self.other_tag ] +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) +- +- def test_should_exclude_with__returns_true_with_other_tag_and_more(self): +- test_patterns = [ +- ([ self.other_tag, "foo" ], "case: first"), +- ([ "foo", self.other_tag ], "case: last"), +- ([ "foo", self.other_tag, "bar" ], "case: middle"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_true_with_similar_tag(self): +- tags = [ self.similar_tag ] +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) +- +- def test_should_exclude_with__returns_true_with_similar_and_more(self): +- test_patterns = [ +- ([ self.similar_tag, "foo" ], "case: first"), +- ([ "foo", self.similar_tag ], "case: last"), +- ([ "foo", self.similar_tag, "bar" ], "case: middle"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_false_without_category_tag(self): +- test_patterns = [ +- ([ ], "case: No tags"), +- ([ "foo" ], "case: One tag"), +- ([ "foo", "bar" ], "case: Two tags"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_run_with__negates_result_of_should_exclude_with(self): +- test_patterns = [ +- ([ ], "case: No tags"), +- ([ "foo" ], "case: One non-category tag"), +- ([ "foo", "bar" ], "case: Two non-category tags"), +- ([ self.enabled_tag ], "case: enabled tag"), +- ([ self.enabled_tag, self.other_tag ], "case: enabled and other tag"), +- ([ self.enabled_tag, "foo" ], "case: enabled and foo tag"), +- ([ self.other_tag ], "case: other tag"), +- ([ self.other_tag, "foo" ], "case: other and foo tag"), +- ([ self.similar_tag ], "case: similar tag"), +- ([ "foo", self.similar_tag ], "case: foo and similar tag"), +- ] +- for tags, case in test_patterns: +- result1 = self.tag_matcher.should_run_with(tags) +- result2 = self.tag_matcher.should_exclude_with(tags) +- self.assertEqual(result1, not result2, "%s: tags=%s" % (case, tags)) +- self.assertEqual(not result1, result2, "%s: tags=%s" % (case, tags)) +- +- def test_make_category_tag__returns_category_tag_prefix_without_value(self): +- category = "xxx" +- tag1 = OnlyWithCategoryTagMatcher.make_category_tag(category) +- tag2 = OnlyWithCategoryTagMatcher.make_category_tag(category, None) +- tag3 = OnlyWithCategoryTagMatcher.make_category_tag(category, value=None) +- self.assertEqual("only.with_xxx=", tag1) +- self.assertEqual("only.with_xxx=", tag2) +- self.assertEqual("only.with_xxx=", tag3) +- self.assertTrue(tag1.startswith(OnlyWithCategoryTagMatcher.tag_prefix)) +- +- def test_make_category_tag__returns_category_tag_with_value(self): +- category = "xxx" +- tag1 = OnlyWithCategoryTagMatcher.make_category_tag(category, "alice") +- tag2 = OnlyWithCategoryTagMatcher.make_category_tag(category, "bob") +- self.assertEqual("only.with_xxx=alice", tag1) +- self.assertEqual("only.with_xxx=bob", tag2) +- +- def test_make_category_tag__returns_category_tag_with_tag_prefix(self): +- my_tag_prefix = "ONLY_WITH." +- category = "xxx" +- TagMatcher = OnlyWithCategoryTagMatcher +- tag0 = TagMatcher.make_category_tag(category, tag_prefix=my_tag_prefix) +- tag1 = TagMatcher.make_category_tag(category, "alice", my_tag_prefix) +- tag2 = TagMatcher.make_category_tag(category, "bob", tag_prefix=my_tag_prefix) +- self.assertEqual("ONLY_WITH.xxx=", tag0) +- self.assertEqual("ONLY_WITH.xxx=alice", tag1) +- self.assertEqual("ONLY_WITH.xxx=bob", tag2) +- self.assertTrue(tag1.startswith(my_tag_prefix)) +- +- def test_ctor__with_tag_prefix(self): +- tag_prefix = "ONLY_WITH." +- tag_matcher = OnlyWithCategoryTagMatcher("xxx", "alice", tag_prefix) +- +- tags = ["foo", "ONLY_WITH.xxx=foo", "only.with_xxx=bar", "bar"] +- actual_tags = tag_matcher.select_category_tags(tags) +- self.assertEqual(["ONLY_WITH.xxx=foo"], actual_tags) +- +- +-class Traits4OnlyWithAnyCategoryTagMatcher(object): +- """Test data for OnlyWithAnyCategoryTagMatcher.""" +- +- TagMatcher0 = OnlyWithCategoryTagMatcher +- TagMatcher = OnlyWithAnyCategoryTagMatcher +- category1_enabled_tag = TagMatcher0.make_category_tag("foo", "alice") +- category1_similar_tag = TagMatcher0.make_category_tag("foo", "alice2") +- category1_disabled_tag = TagMatcher0.make_category_tag("foo", "bob") +- category2_enabled_tag = TagMatcher0.make_category_tag("bar", "BOB") +- category2_similar_tag = TagMatcher0.make_category_tag("bar", "BOB2") +- category2_disabled_tag = TagMatcher0.make_category_tag("bar", "CHARLY") +- unknown_category_tag = TagMatcher0.make_category_tag("UNKNOWN", "one") +- +- +-class TestOnlyWithAnyCategoryTagMatcher(TestCase): +- TagMatcher = OnlyWithAnyCategoryTagMatcher +- traits = Traits4OnlyWithAnyCategoryTagMatcher +- +- def setUp(self): +- value_provider = { +- "foo": "alice", +- "bar": "BOB", +- } +- with warnings.catch_warnings(): +- warnings.simplefilter("ignore", DeprecationWarning) +- self.tag_matcher = self.TagMatcher(value_provider) +- +- # def test_deprecating_warning_is_issued(self): +- # value_provider = {"foo": "alice"} +- # with warnings.catch_warnings(record=True) as recorder: +- # warnings.simplefilter("always", DeprecationWarning) +- # tag_matcher = OnlyWithAnyCategoryTagMatcher(value_provider) +- # self.assertEqual(len(recorder), 1) +- # last_warning = recorder[-1] +- # assert issubclass(last_warning.category, DeprecationWarning) +- # assert "deprecated" in str(last_warning.message) +- +- def test_should_exclude_with__returns_false_with_enabled_tag(self): +- traits = self.traits +- tags1 = [ traits.category1_enabled_tag ] +- tags2 = [ traits.category2_enabled_tag ] +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags1)) +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags2)) +- +- def test_should_exclude_with__returns_false_with_enabled_tag_and_more(self): +- traits = self.traits +- test_patterns = [ +- ([ traits.category1_enabled_tag, traits.category1_disabled_tag ], "case: first"), +- ([ traits.category1_disabled_tag, traits.category1_enabled_tag ], "case: last"), +- ([ "foo", traits.category1_enabled_tag, traits.category1_disabled_tag, "bar" ], "case: middle"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_true_with_other_tag(self): +- traits = self.traits +- tags = [ traits.category1_disabled_tag ] +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) +- +- def test_should_exclude_with__returns_true_with_other_tag_and_more(self): +- traits = self.traits +- test_patterns = [ +- ([ traits.category1_disabled_tag, "foo" ], "case: first"), +- ([ "foo", traits.category1_disabled_tag ], "case: last"), +- ([ "foo", traits.category1_disabled_tag, "bar" ], "case: middle"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_true_with_similar_tag(self): +- traits = self.traits +- tags = [ traits.category1_similar_tag ] +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags)) +- +- def test_should_exclude_with__returns_true_with_similar_and_more(self): +- traits = self.traits +- test_patterns = [ +- ([ traits.category1_similar_tag, "foo" ], "case: first"), +- ([ "foo", traits.category1_similar_tag ], "case: last"), +- ([ "foo", traits.category1_similar_tag, "bar" ], "case: middle"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(True, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_false_without_category_tag(self): +- test_patterns = [ +- ([ ], "case: No tags"), +- ([ "foo" ], "case: One tag"), +- ([ "foo", "bar" ], "case: Two tags"), +- ] +- for tags, case in test_patterns: +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags), +- "%s: tags=%s" % (case, tags)) +- +- def test_should_exclude_with__returns_false_with_unknown_category_tag(self): +- """Tags from unknown categories, not supported by value_provider, +- should not be excluded. +- """ +- traits = self.traits +- tags = [ traits.unknown_category_tag ] +- self.assertEqual("only.with_UNKNOWN=one", traits.unknown_category_tag) +- self.assertEqual(None, self.tag_matcher.value_provider.get("UNKNOWN")) +- self.assertEqual(False, self.tag_matcher.should_exclude_with(tags)) +- +- def test_should_exclude_with__combinations_of_2_categories(self): +- traits = self.traits +- test_patterns = [ +- ("case 00: 2 disabled category tags", True, +- [ traits.category1_disabled_tag, traits.category2_disabled_tag]), +- ("case 01: disabled and enabled category tags", True, +- [ traits.category1_disabled_tag, traits.category2_enabled_tag]), +- ("case 10: enabled and disabled category tags", True, +- [ traits.category1_enabled_tag, traits.category2_disabled_tag]), +- ("case 11: 2 enabled category tags", False, # -- SHOULD-RUN +- [ traits.category1_enabled_tag, traits.category2_enabled_tag]), +- # -- SPECIAL CASE: With unknown category +- ("case 0x: disabled and unknown category tags", True, +- [ traits.category1_disabled_tag, traits.unknown_category_tag]), +- ("case 1x: enabled and unknown category tags", False, # SHOULD-RUN +- [ traits.category1_enabled_tag, traits.unknown_category_tag]), +- ] +- for case, expected, tags in test_patterns: +- actual_result = self.tag_matcher.should_exclude_with(tags) +- self.assertEqual(expected, actual_result, +- "%s: tags=%s" % (case, tags)) +- +- def test_should_run_with__negates_result_of_should_exclude_with(self): +- traits = self.traits +- test_patterns = [ +- ([ ], "case: No tags"), +- ([ "foo" ], "case: One non-category tag"), +- ([ "foo", "bar" ], "case: Two non-category tags"), +- ([ traits.category1_enabled_tag ], "case: enabled tag"), +- ([ traits.category1_enabled_tag, traits.category1_disabled_tag ], "case: enabled and other tag"), +- ([ traits.category1_enabled_tag, "foo" ], "case: enabled and foo tag"), +- ([ traits.category1_disabled_tag ], "case: other tag"), +- ([ traits.category1_disabled_tag, "foo" ], "case: other and foo tag"), +- ([ traits.category1_similar_tag ], "case: similar tag"), +- ([ "foo", traits.category1_similar_tag ], "case: foo and similar tag"), +- ] +- for tags, case in test_patterns: +- result1 = self.tag_matcher.should_run_with(tags) +- result2 = self.tag_matcher.should_exclude_with(tags) +- self.assertEqual(result1, not result2, "%s: tags=%s" % (case, tags)) +- self.assertEqual(not result1, result2, "%s: tags=%s" % (case, tags)) +- diff --git a/meta-python/recipes-devtools/python/python3-behave/0027-Comment-tweaks.patch b/meta-python/recipes-devtools/python/python3-behave/0027-Comment-tweaks.patch new file mode 100644 index 000000000..c41068c96 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0027-Comment-tweaks.patch @@ -0,0 +1,30 @@ +From 362e778e5e57054b45099f1432abea181987d3c9 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 19:24:14 +0200 +Subject: [PATCH] Comment tweaks + +--- + behave/runner.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/behave/runner.py b/behave/runner.py +index f0f077a..f209cb0 100644 +--- a/behave/runner.py ++++ b/behave/runner.py +@@ -167,10 +167,15 @@ class Context(object): + self._record = {} + self._origin = {} + self._mode = self.BEHAVE ++ ++ # -- MODEL ENTITY REFERENCES/SUPPORT: + self.feature = None +- # -- RECHECK: If needed ++ # DISABLED: self.rule = None ++ # DISABLED: self.scenario = None + self.text = None + self.table = None ++ ++ # -- RUNTIME SUPPORT: + self.stdout_capture = None + self.stderr_capture = None + self.log_capture = None diff --git a/meta-python/recipes-devtools/python/python3-behave/0028-FIX-warnings-related-to-invalid-escapes-in-regex.patch b/meta-python/recipes-devtools/python/python3-behave/0028-FIX-warnings-related-to-invalid-escapes-in-regex.patch new file mode 100644 index 000000000..8f0ad3a82 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0028-FIX-warnings-related-to-invalid-escapes-in-regex.patch @@ -0,0 +1,79 @@ +From d892f81e3bd3e979d3677c39c3a9efc40131b452 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sun, 9 Jun 2019 19:32:10 +0200 +Subject: [PATCH] FIX warnings related to invalid escapes in regex. + +--- + behave4cmd0/command_shell_proc.py | 3 ++- + features/steps/behave_select_files_steps.py | 24 +++++++++++++-------- + 2 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/behave4cmd0/command_shell_proc.py b/behave4cmd0/command_shell_proc.py +index 07f1c84..03ca55a 100755 +--- a/behave4cmd0/command_shell_proc.py ++++ b/behave4cmd0/command_shell_proc.py +@@ -251,6 +251,7 @@ class BehaveWinCommandOutputProcessor(LineCommandOutputProcessor): + "No such file or directory: '(?P.*)'", + "[Errno 2] No such file or directory:"), # IOError + ExceptionWithPathNormalizer( +- '^\s*File "(?P.*)", line \d+, in ', ++ # WAS: '^\s*File "(?P.*)", line \d+, in ', ++ r'^\s*File "(?P.*)", line \d+, in ', + 'File "'), + ] +diff --git a/features/steps/behave_select_files_steps.py b/features/steps/behave_select_files_steps.py +index 431674e..0d2cd2e 100644 +--- a/features/steps/behave_select_files_steps.py ++++ b/features/steps/behave_select_files_steps.py +@@ -1,29 +1,34 @@ + # -*- coding: utf-8 -*- +-""" ++# DOCSTRING-NEEDS-REGEX-STRING-PREFIX: Due to example w/ wildcard pattern. ++r''' + Provides step definitions that test how the behave runner selects feature files. + + EXAMPLE: ++ ++.. code-block:: gherkin ++ + Given behave has the following feature fileset: +- ''' ++ """ + features/alice.feature + features/bob.feature + features/barbi.feature +- ''' ++ """ + When behave includes feature files with "features/a.*\.feature" + And behave excludes feature files with "features/b.*\.feature" + Then the following feature files are selected: +- ''' ++ """ + features/alice.feature +- ''' +-""" ++ """ ++''' + + from __future__ import absolute_import +-from behave import given, when, then +-from behave.runner_util import FeatureListParser +-from hamcrest import assert_that, equal_to + from copy import copy + import re + import six ++from hamcrest import assert_that, equal_to ++from behave import given, when, then ++from behave.runner_util import FeatureListParser ++ + + # ----------------------------------------------------------------------------- + # STEP UTILS: +@@ -47,6 +52,7 @@ class BasicBehaveRunner(object): + # ----------------------------------------------------------------------------- + # STEP DEFINITIONS: + # ----------------------------------------------------------------------------- ++# pylint: disable=invalid-name + @given('behave has the following feature fileset') + def step_given_behave_has_feature_fileset(context): + assert context.text is not None, "REQUIRE: text" diff --git a/meta-python/recipes-devtools/python/python3-behave/0029-Steps-catalog-should-not-break-configured-rerun-sett.patch b/meta-python/recipes-devtools/python/python3-behave/0029-Steps-catalog-should-not-break-configured-rerun-sett.patch new file mode 100644 index 000000000..e2d86ca99 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0029-Steps-catalog-should-not-break-configured-rerun-sett.patch @@ -0,0 +1,73 @@ +From 11c4ac862bdce901ef19864532ef6d17a85799cd Mon Sep 17 00:00:00 2001 +From: Edvin Linge +Date: Mon, 31 Jul 2017 17:05:18 +0200 +Subject: [PATCH] Steps-catalog should not break configured rerun settings + +--- + behave/configuration.py | 5 ++++- + features/formatter.rerun.feature | 17 +++++++++++++++++ + features/formatter.steps_catalog.feature | 13 +++++++++++++ + 3 files changed, 34 insertions(+), 1 deletion(-) + +diff --git a/behave/configuration.py b/behave/configuration.py +index 49b1dff..861f89f 100644 +--- a/behave/configuration.py ++++ b/behave/configuration.py +@@ -601,7 +601,10 @@ class Configuration(object): + if self.steps_catalog: + # -- SHOW STEP-CATALOG: As step summary. + self.default_format = "steps.catalog" +- self.format = ["steps.catalog"] ++ if self.format: ++ self.format.append("steps.catalog") ++ else: ++ self.format = ["steps.catalog"] + self.dry_run = True + self.summary = False + self.show_skipped = False +diff --git a/features/formatter.rerun.feature b/features/formatter.rerun.feature +index 1e645f1..b9d7e1b 100644 +--- a/features/formatter.rerun.feature ++++ b/features/formatter.rerun.feature +@@ -294,3 +294,20 @@ Feature: Rerun Formatter + features/bob.feature:13 + """ + And note that "the second RerunFormatter overwrites the output of the first one" ++ ++ @with.behave_configfile ++ Scenario: RerunFormatter with steps-catalog ++ Given a file named "behave.ini" with: ++ """ ++ [behave] ++ format = rerun ++ outfiles = rerun.txt ++ """ ++ When I run "behave --steps-catalog features/" ++ ++ Then it should pass with: ++ """ ++ Given a step passes ++ """ ++ And a file named "rerun.txt" does not exist ++ +diff --git a/features/formatter.steps_catalog.feature b/features/formatter.steps_catalog.feature +index 09591bf..936d7f4 100644 +--- a/features/formatter.steps_catalog.feature ++++ b/features/formatter.steps_catalog.feature +@@ -98,3 +98,16 @@ Feature: Steps Catalog Formatter + """ + But note that "the step definitions are ordered by step type" + And note that "'When I visit {person}' has no doc-string" ++ ++ ++ Scenario: Steps catalog formatter is used for output even when other formatter is specified ++ When I run "behave --steps-catalog -f plain features/" ++ Then it should pass with: ++ """ ++ Given {person} lives in {city} ++ Setup the data where a person lives and store in the database. ++ ++ :param person: Person's name (as string). ++ :param city: City where the person lives (as string). ++ """ ++ diff --git a/meta-python/recipes-devtools/python/python3-behave/0030-Add-feature-w-rules-and-failing-scenarios-enable-via.patch b/meta-python/recipes-devtools/python/python3-behave/0030-Add-feature-w-rules-and-failing-scenarios-enable-via.patch new file mode 100644 index 000000000..f050c9c85 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0030-Add-feature-w-rules-and-failing-scenarios-enable-via.patch @@ -0,0 +1,36 @@ +From 502382430f1bee8d0a9031fc70541488224a1029 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Mon, 1 Jul 2019 22:07:19 +0200 +Subject: [PATCH] Add feature w/ rules and failing scenarios (enable via + tags=fail or tags=xfail). + +--- + .../gherkin_v6/features/rule_fails.feature | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + create mode 100644 examples/gherkin_v6/features/rule_fails.feature + +diff --git a/examples/gherkin_v6/features/rule_fails.feature b/examples/gherkin_v6/features/rule_fails.feature +new file mode 100644 +index 0000000..7e3872e +--- /dev/null ++++ b/examples/gherkin_v6/features/rule_fails.feature +@@ -0,0 +1,19 @@ ++@fail ++Feature: With Rule(s) and Failing Scenario(s) ++ ++ HINT: Contains failing scenarios (by intention). ++ ++ @xfail ++ Scenario: F0 -- Fails ++ When some step fails ++ ++ Rule: Fails in Scenario F2 ++ ++ Scenario: F1 ++ Given a step passes ++ ++ @xfail ++ Scenario: F2 -- Fails ++ Given another step passes ++ When a step fails ++ Then another step passes diff --git a/meta-python/recipes-devtools/python/python3-behave/0031-examples-gherkin_v6-Tweak-ScenarioOutline-Examples-t.patch b/meta-python/recipes-devtools/python/python3-behave/0031-examples-gherkin_v6-Tweak-ScenarioOutline-Examples-t.patch new file mode 100644 index 000000000..6f71729f5 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0031-examples-gherkin_v6-Tweak-ScenarioOutline-Examples-t.patch @@ -0,0 +1,27 @@ +From dcbba864209e8784ecaa5a1cf80b346b39b2a5d6 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Mon, 1 Jul 2019 22:11:13 +0200 +Subject: [PATCH] examples/gherkin_v6: Tweak ScenarioOutline Examples titles. + +--- + examples/gherkin_v6/features/rule_2.feature | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/examples/gherkin_v6/features/rule_2.feature b/examples/gherkin_v6/features/rule_2.feature +index a802e19..8b8bb04 100644 +--- a/examples/gherkin_v6/features/rule_2.feature ++++ b/examples/gherkin_v6/features/rule_2.feature +@@ -36,7 +36,12 @@ Feature: Gherkin v6 Example -- with Rules + Scenario Template: R3.Scenario + Given a person named "" + +- Examples: ++ Examples: R3.E1 + | name | + | Alice | + | Bob | ++ ++ Examples: R3.E2 ++ | name | ++ | Charly | ++ | Doro | diff --git a/meta-python/recipes-devtools/python/python3-behave/0032-Add-info-on-merged-pull-588.patch b/meta-python/recipes-devtools/python/python3-behave/0032-Add-info-on-merged-pull-588.patch new file mode 100644 index 000000000..4d44fea08 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0032-Add-info-on-merged-pull-588.patch @@ -0,0 +1,21 @@ +From 9c9bb1e6320a6ffbac0b171d9cf98cbb311104eb Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Mon, 1 Jul 2019 08:06:43 +0200 +Subject: [PATCH] Add info on merged pull #588 + +--- + CHANGES.rst | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/CHANGES.rst b/CHANGES.rst +index a91e22a..3d805b3 100644 +--- a/CHANGES.rst ++++ b/CHANGES.rst +@@ -36,6 +36,7 @@ PARTIALLY FIXED: + + FIXED: + ++* pull #588: Steps-catalog argument should not break configured rerun settings (provided by: Lego3) + * issue #725: Scenario Outline description lines seem to be ignored (submitted by: nizwiz) + * issue #713: Background section doesn't support description (provided by: dgou) + * pull #657: Allow async steps with timeouts to fail when they raise exceptions (provided by: ALSchwalm) diff --git a/meta-python/recipes-devtools/python/python3-behave/0033-Tweak-tests-required-by-pytest-5.0.patch b/meta-python/recipes-devtools/python/python3-behave/0033-Tweak-tests-required-by-pytest-5.0.patch new file mode 100644 index 000000000..1fdda3a7d --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0033-Tweak-tests-required-by-pytest-5.0.patch @@ -0,0 +1,97 @@ +From 8fe1bc305aeea5d86de1b3c5dfcd2e7984fc5145 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Fri, 5 Jul 2019 08:27:44 +0200 +Subject: [PATCH] Tweak tests, required by pytest >= 5.0. With pytest.raises + use str(e.value) instead of str(e) in some cases. + +--- + tests/issues/test_issue0458.py | 2 +- + tests/unit/test_context_cleanups.py | 2 +- + tests/unit/test_textutil.py | 24 +++++++++++++++--------- + 3 files changed, 17 insertions(+), 11 deletions(-) + +diff --git a/tests/issues/test_issue0458.py b/tests/issues/test_issue0458.py +index 1853ad6..f66f6d3 100644 +--- a/tests/issues/test_issue0458.py ++++ b/tests/issues/test_issue0458.py +@@ -48,7 +48,7 @@ def test_issue(exception_class, message): + raise_exception(exception_class, message) + + # -- SHOULD NOT RAISE EXCEPTION HERE: +- text = _text(e) ++ text = _text(e.value) + # -- DIAGNOSTICS: + print(u"text"+ text) + print(u"exception: %s" % e) +diff --git a/tests/unit/test_context_cleanups.py b/tests/unit/test_context_cleanups.py +index b0e8ae6..bf0ab50 100644 +--- a/tests/unit/test_context_cleanups.py ++++ b/tests/unit/test_context_cleanups.py +@@ -153,7 +153,7 @@ class TestContextCleanup(object): + with pytest.raises(AssertionError) as e: + with scoped_context_layer(context): + context.add_cleanup(non_callable) +- assert "REQUIRES: callable(cleanup_func)" in str(e) ++ assert "REQUIRES: callable(cleanup_func)" in str(e.value) + + def test_on_cleanup_error__prints_error_by_default(self, capsys): + def bad_cleanup_func(): +diff --git a/tests/unit/test_textutil.py b/tests/unit/test_textutil.py +index f7f642c..e05e9ad 100644 +--- a/tests/unit/test_textutil.py ++++ b/tests/unit/test_textutil.py +@@ -214,9 +214,11 @@ class TestObjectToTextConversion(object): + with pytest.raises(AssertionError) as e: + assert False, message + +- text2 = text(e) +- expected = u"AssertionError: %s" % message +- assert text2.endswith(expected) ++ # -- FOR: pytest < 5.0 ++ # expected = u"AssertionError: %s" % message ++ text2 = text(e.value) ++ assert u"AssertionError" in text(e) ++ assert message in text2, "OOPS: text=%r" % text2 + + @requires_python2 + @pytest.mark.parametrize("message", [ +@@ -236,10 +238,11 @@ class TestObjectToTextConversion(object): + assert expected_decode_error in str(uni_error) + assert False, bytes_message.decode(self.ENCODING) + ++ # -- FOR: pytest < 5.0 ++ # expected = u"AssertionError: %s" % message + print("decode_error_occured(ascii)=%s" % decode_error_occured) +- text2 = text(e) +- expected = u"AssertionError: %s" % message +- assert text2.endswith(expected) ++ text2 = text(e.value) ++ assert message in text2, "OOPS: text=%r" % text2 + + @pytest.mark.parametrize("exception_class, message", [ + (AssertionError, u"Ärgernis"), +@@ -251,10 +254,13 @@ class TestObjectToTextConversion(object): + with pytest.raises(exception_class) as e: + raise exception_class(message) + +- text2 = text(e) ++ # -- FOR: pytest < 5.0 ++ # expected = u"AssertionError: %s" % message ++ text2 = text(e.value) + expected = u"%s: %s" % (exception_class.__name__, message) + assert isinstance(text2, six.text_type) +- assert text2.endswith(expected) ++ assert exception_class.__name__ in str(e) ++ assert message in text2, "OOPS: text=%r" % text2 + + @requires_python2 + @pytest.mark.parametrize("exception_class, message", [ +@@ -268,7 +274,7 @@ class TestObjectToTextConversion(object): + with pytest.raises(exception_class) as e: + raise exception_class(bytes_message) + +- text2 = text(e) ++ text2 = text(e.value) + unicode_message = bytes_message.decode(self.ENCODING) + expected = u"%s: %s" % (exception_class.__name__, unicode_message) + assert isinstance(text2, six.text_type) diff --git a/meta-python/recipes-devtools/python/python3-behave/0034-CLEANUP-Use-pytest-instead-of-nose-to-remove-nose.im.patch b/meta-python/recipes-devtools/python/python3-behave/0034-CLEANUP-Use-pytest-instead-of-nose-to-remove-nose.im.patch new file mode 100644 index 000000000..7a47c7fe7 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0034-CLEANUP-Use-pytest-instead-of-nose-to-remove-nose.im.patch @@ -0,0 +1,180 @@ +From dce0e40c9689a8d4fe71c9b2e8e4decd72dcf574 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Fri, 5 Jul 2019 08:45:51 +0200 +Subject: [PATCH] CLEANUP: Use pytest instead of nose to remove nose.importer + DeprecationWarning. + +--- + tests/unit/test_importer.py | 163 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 163 insertions(+) + create mode 100644 tests/unit/test_importer.py + +diff --git a/tests/unit/test_importer.py b/tests/unit/test_importer.py +new file mode 100644 +index 0000000..f3f4e2c +--- /dev/null ++++ b/tests/unit/test_importer.py +@@ -0,0 +1,163 @@ ++# -*- coding: utf-8 -*- ++""" ++Tests for behave.importing. ++The module provides a lazy-loading/importing mechanism. ++""" ++ ++from __future__ import absolute_import ++import pytest ++from behave.importer import LazyObject, LazyDict, load_module, parse_scoped_name ++from behave.formatter.base import Formatter ++import sys ++import types ++# import unittest ++ ++ ++class TestTheory(object): ++ """Marker for test-theory classes as syntactic sugar.""" ++ pass ++ ++ ++class ImportModuleTheory(TestTheory): ++ """ ++ Provides a test theory for importing modules. ++ """ ++ ++ @classmethod ++ def ensure_module_is_not_imported(cls, module_name): ++ if module_name in sys.modules: ++ del sys.modules[module_name] ++ cls.assert_module_is_not_imported(module_name) ++ ++ @staticmethod ++ def assert_module_is_imported(module_name): ++ module = sys.modules.get(module_name, None) ++ assert module_name in sys.modules ++ assert module is not None ++ ++ @staticmethod ++ def assert_module_is_not_imported(module_name): ++ assert module_name not in sys.modules ++ ++ @staticmethod ++ def assert_module_with_name(module, name): ++ assert isinstance(module, types.ModuleType) ++ assert module.__name__ == name ++ ++ ++class TestLoadModule(object): ++ theory = ImportModuleTheory ++ ++ def test_load_module__should_fail_for_unknown_module(self): ++ with pytest.raises(ImportError) as e: ++ load_module("__unknown_module__") ++ # OLD: assert_raises(ImportError, load_module, "__unknown_module__") ++ ++ def test_load_module__should_succeed_for_already_imported_module(self): ++ module_name = "behave.importer" ++ self.theory.assert_module_is_imported(module_name) ++ ++ module = load_module(module_name) ++ self.theory.assert_module_with_name(module, module_name) ++ self.theory.assert_module_is_imported(module_name) ++ ++ def test_load_module__should_succeed_for_existing_module(self): ++ module_name = "test._importer_candidate" ++ self.theory.ensure_module_is_not_imported(module_name) ++ ++ module = load_module(module_name) ++ self.theory.assert_module_with_name(module, module_name) ++ self.theory.assert_module_is_imported(module_name) ++ ++ ++class TestLazyObject(object): ++ ++ def test_get__should_succeed_for_known_object(self): ++ lazy = LazyObject("behave.importer", "LazyObject") ++ value = lazy.get() ++ assert value is LazyObject ++ ++ lazy2 = LazyObject("behave.importer:LazyObject") ++ value2 = lazy2.get() ++ assert value2 is LazyObject ++ ++ lazy3 = LazyObject("behave.formatter.steps", "StepsFormatter") ++ value3 = lazy3.get() ++ assert issubclass(value3, Formatter) ++ ++ def test_get__should_fail_for_unknown_module(self): ++ lazy = LazyObject("__unknown_module__", "xxx") ++ with pytest.raises(ImportError): ++ lazy.get() ++ ++ def test_get__should_fail_for_unknown_object_in_module(self): ++ lazy = LazyObject("test._importer_candidate", "xxx") ++ with pytest.raises(ImportError): ++ lazy.get() ++ ++ ++class LazyDictTheory(TestTheory): ++ ++ @staticmethod ++ def safe_getitem(data, key): ++ return dict.__getitem__(data, key) ++ ++ @classmethod ++ def assert_item_is_lazy(cls, data, key): ++ value = cls.safe_getitem(data, key) ++ cls.assert_is_lazy_object(value) ++ ++ @classmethod ++ def assert_item_is_not_lazy(cls, data, key): ++ value = cls.safe_getitem(data, key) ++ cls.assert_is_not_lazy_object(value) ++ ++ @staticmethod ++ def assert_is_lazy_object(obj): ++ assert isinstance(obj, LazyObject) ++ ++ @staticmethod ++ def assert_is_not_lazy_object(obj): ++ assert not isinstance(obj, LazyObject) ++ ++ ++class TestLazyDict(object): ++ theory = LazyDictTheory ++ ++ def test_unknown_item_access__should_raise_keyerror(self): ++ lazy_dict = LazyDict({"alice": 42}) ++ item_access = lambda key: lazy_dict[key] ++ with pytest.raises(KeyError): ++ item_access("unknown") ++ ++ def test_plain_item_access__should_succeed(self): ++ theory = LazyDictTheory ++ lazy_dict = LazyDict({"alice": 42}) ++ theory.assert_item_is_not_lazy(lazy_dict, "alice") ++ ++ value = lazy_dict["alice"] ++ assert value == 42 ++ ++ def test_lazy_item_access__should_load_object(self): ++ ImportModuleTheory.ensure_module_is_not_imported("inspect") ++ lazy_dict = LazyDict({"alice": LazyObject("inspect:ismodule")}) ++ self.theory.assert_item_is_lazy(lazy_dict, "alice") ++ self.theory.assert_item_is_lazy(lazy_dict, "alice") ++ ++ value = lazy_dict["alice"] ++ self.theory.assert_is_not_lazy_object(value) ++ self.theory.assert_item_is_not_lazy(lazy_dict, "alice") ++ ++ def test_lazy_item_access__should_fail_with_unknown_module(self): ++ lazy_dict = LazyDict({"bob": LazyObject("__unknown_module__", "xxx")}) ++ item_access = lambda key: lazy_dict[key] ++ with pytest.raises(ImportError): ++ item_access("bob") ++ ++ def test_lazy_item_access__should_fail_with_unknown_object(self): ++ lazy_dict = LazyDict({ ++ "bob": LazyObject("behave.importer", "XUnknown") ++ }) ++ item_access = lambda key: lazy_dict[key] ++ with pytest.raises(ImportError): ++ item_access("bob") diff --git a/meta-python/recipes-devtools/python/python3-behave/0035-REMOVE-DEPENDENCY-nose-to-avoid-nose.importer-warnin.patch b/meta-python/recipes-devtools/python/python3-behave/0035-REMOVE-DEPENDENCY-nose-to-avoid-nose.importer-warnin.patch new file mode 100644 index 000000000..33738459d --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0035-REMOVE-DEPENDENCY-nose-to-avoid-nose.importer-warnin.patch @@ -0,0 +1,1815 @@ +From 65b17ccbea1a17b26e6191a1d7336088d0dd4181 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 09:10:52 +0200 +Subject: [PATCH] REMOVE-DEPENDENCY: nose (to avoid nose.importer warnings) + +Replace nose test-framework functionality w/ pytest. +Move test/*.py => tests/unit/*.py +--- + MANIFEST.in | 2 - + conftest.py | 13 ++ + invoke.yaml | 1 + + py.requirements/ci.tox.txt | 9 + + py.requirements/ci.travis.txt | 1 - + py.requirements/develop.txt | 1 - + py.requirements/testing.txt | 3 +- + pytest.ini | 7 +- + setup.py | 8 +- + tasks/test.py | 2 +- + test/__init__.py | 0 + test/test_importer.py | 151 ------------- + {test => tests/unit}/_importer_candidate.py | 0 + tests/unit/reporter/test_summary.py | 2 - + .../test_tag_expression_v1_part1.py | 17 +- + .../test_tag_expression_v1_part2.py | 18 +- + {test => tests/unit}/test_ansi_escapes.py | 14 +- + {test => tests/unit}/test_configuration.py | 75 +++--- + {test => tests/unit}/test_formatter.py | 15 +- + .../unit}/test_formatter_progress.py | 7 + + {test => tests/unit}/test_formatter_rerun.py | 12 +- + {test => tests/unit}/test_formatter_tags.py | 9 + + tests/unit/test_importer.py | 2 +- + {test => tests/unit}/test_log_capture.py | 9 +- + {test => tests/unit}/test_matchers.py | 24 +- + tests/unit/test_parser.py | 1 - + {test => tests/unit}/test_runner.py | 213 ++++++++++-------- + {test => tests/unit}/test_step_registry.py | 5 +- + tests/unit/test_textutil.py | 12 +- + tox.ini | 19 +- + 30 files changed, 283 insertions(+), 369 deletions(-) + create mode 100644 py.requirements/ci.tox.txt + delete mode 100644 test/__init__.py + delete mode 100644 test/test_importer.py + rename {test => tests/unit}/_importer_candidate.py (100%) + rename {test => tests/unit}/test_ansi_escapes.py (84%) + rename {test => tests/unit}/test_configuration.py (72%) + rename {test => tests/unit}/test_formatter.py (94%) + rename {test => tests/unit}/test_formatter_progress.py (99%) + rename {test => tests/unit}/test_formatter_rerun.py (94%) + rename {test => tests/unit}/test_formatter_tags.py (99%) + rename {test => tests/unit}/test_log_capture.py (87%) + rename {test => tests/unit}/test_matchers.py (93%) + rename {test => tests/unit}/test_runner.py (82%) + rename {test => tests/unit}/test_step_registry.py (95%) + +diff --git a/MANIFEST.in b/MANIFEST.in +index 49cb7c6..60c2601 100644 +--- a/MANIFEST.in ++++ b/MANIFEST.in +@@ -33,5 +33,3 @@ recursive-include py.requirements *.txt + + prune .tox + prune .venv* +-prune paver_ext +-exclude pavement.py +diff --git a/conftest.py b/conftest.py +index 7b3a883..71a3bd0 100644 +--- a/conftest.py ++++ b/conftest.py +@@ -10,6 +10,10 @@ Add project-specific information. + import behave + import pytest + ++ ++# --------------------------------------------------------------------------- ++# PYTEST FIXTURES: ++# --------------------------------------------------------------------------- + @pytest.fixture(autouse=True) + def _annotate_environment(request): + """Add project-specific information to test-run environment: +@@ -25,3 +29,12 @@ def _annotate_environment(request): + behave_version = behave.__version__ + environment.append(("behave", behave_version)) + ++_pytest_version = pytest.__version__ ++if _pytest_version >= "5.0": ++ # -- SUPPORTED SINCE: pytest 5.0 ++ @pytest.fixture(scope="session", autouse=True) ++ def log_global_env_facts(record_testsuite_property): ++ # SEE: https://docs.pytest.org/en/latest/usage.html ++ behave_version = behave.__version__ ++ record_testsuite_property("BEHAVE_VERSION", behave_version) ++ +diff --git a/invoke.yaml b/invoke.yaml +index 4f21328..3e93cfc 100644 +--- a/invoke.yaml ++++ b/invoke.yaml +@@ -23,6 +23,7 @@ sphinx: + languages: + - de + # PREPARED: - zh-CN ++ + cleanup: + extra_directories: + - "build" +diff --git a/py.requirements/ci.tox.txt b/py.requirements/ci.tox.txt +new file mode 100644 +index 0000000..6b3b3ae +--- /dev/null ++++ b/py.requirements/ci.tox.txt +@@ -0,0 +1,9 @@ ++# ============================================================================ ++# BEHAVE: PYTHON PACKAGE REQUIREMENTS: ci.tox.txt ++# ============================================================================ ++ ++pytest >= 4.2 ++pytest-html >= 1.19.0 ++mock >= 2.0 ++PyHamcrest >= 1.9 ++path.py >= 10.1 +diff --git a/py.requirements/ci.travis.txt b/py.requirements/ci.travis.txt +index 1cc0239..73d65f6 100644 +--- a/py.requirements/ci.travis.txt ++++ b/py.requirements/ci.travis.txt +@@ -1,5 +1,4 @@ + mock +-nose + PyHamcrest >= 1.9 + pytest >= 3.0 + pytest-html >= 1.19.0 +diff --git a/py.requirements/develop.txt b/py.requirements/develop.txt +index 3deedc7..d823389 100644 +--- a/py.requirements/develop.txt ++++ b/py.requirements/develop.txt +@@ -14,7 +14,6 @@ pycmd + bumpversion >= 0.4.0 + + # -- DEVELOPMENT SUPPORT: +-# PREPARED: nose-cov >= 1.4 + tox >= 1.8.1 + coverage >= 4.2 + pytest-cov +diff --git a/py.requirements/testing.txt b/py.requirements/testing.txt +index 3806d39..a418739 100644 +--- a/py.requirements/testing.txt ++++ b/py.requirements/testing.txt +@@ -4,9 +4,8 @@ + + # -- TESTING: Unit tests and behave self-tests. + # PREPARED-FUTURE: behave4cmd0, behave4cmd +-pytest >= 3.0 ++pytest >= 4.2 + pytest-html >= 1.19.0 +-nose >= 1.3 + mock >= 2.0 + PyHamcrest >= 1.9 + +diff --git a/pytest.ini b/pytest.ini +index 17ad388..a686596 100644 +--- a/pytest.ini ++++ b/pytest.ini +@@ -16,8 +16,8 @@ + # ============================================================================ + + [pytest] +-minversion = 2.8 +-testpaths = test tests ++minversion = 4.2 ++testpaths = tests + python_files = test_*.py + addopts = --metadata PACKAGE_UNDER_TEST behave + --metadata PACKAGE_VERSION 1.2.7.dev1 +@@ -26,6 +26,7 @@ addopts = --metadata PACKAGE_UNDER_TEST behave + markers = + smoke + slow ++ + # -- BACKWARD COMPATIBILITY: pytest < 2.8 +-norecursedirs = .git .tox build dist py.requirements tmp* _WORKSPACE ++# norecursedirs = .git .tox build dist py.requirements tmp* _WORKSPACE + +diff --git a/setup.py b/setup.py +index ac7bddf..9c7560d 100644 +--- a/setup.py ++++ b/setup.py +@@ -86,13 +86,11 @@ setup( + "win_unicode_console; python_version < '3.6'", + "colorama", + ], +- test_suite="nose.collector", + tests_require=[ +- "pytest >= 3.0", ++ "pytest >= 4.2", + "pytest-html >= 1.19.0", +- "nose >= 1.3", + "mock >= 1.1", +- "PyHamcrest >= 1.8", ++ "PyHamcrest >= 1.9", + "path.py >= 11.5.0" + ], + cmdclass = { +@@ -105,7 +103,7 @@ setup( + ], + "develop": [ + "coverage", +- "pytest >= 3.0", ++ "pytest >= 4.2", + "pytest-html >= 1.19.0", + "pytest-cov", + "tox", +diff --git a/tasks/test.py b/tasks/test.py +index 06eb175..bfa2d80 100644 +--- a/tasks/test.py ++++ b/tasks/test.py +@@ -172,7 +172,7 @@ namespace.configure({ + }, + }, + "pytest": { +- "scopes": ["test", "tests"], ++ "scopes": ["tests"], + "args": "", + "options": "", # -- NOTE: Overide in configfile "invoke.yaml" + }, +diff --git a/test/__init__.py b/test/__init__.py +deleted file mode 100644 +index e69de29..0000000 +diff --git a/test/test_importer.py b/test/test_importer.py +deleted file mode 100644 +index baca21a..0000000 +--- a/test/test_importer.py ++++ /dev/null +@@ -1,151 +0,0 @@ +-# -*- coding: utf-8 -*- +-""" +-Tests for behave.importing. +-The module provides a lazy-loading/importing mechanism. +-""" +- +-from __future__ import absolute_import +-from behave.importer import LazyObject, LazyDict, load_module, parse_scoped_name +-from behave.formatter.base import Formatter +-from nose.tools import eq_, assert_raises +-import sys +-import types +-# import unittest +- +- +-class TestTheory(object): pass +-class ImportModuleTheory(TestTheory): +- """ +- Provides a test theory for importing modules. +- """ +- +- @classmethod +- def ensure_module_is_not_imported(cls, module_name): +- if module_name in sys.modules: +- del sys.modules[module_name] +- cls.assert_module_is_not_imported(module_name) +- +- @staticmethod +- def assert_module_is_imported(module_name): +- module = sys.modules.get(module_name, None) +- assert module_name in sys.modules +- assert module is not None +- +- @staticmethod +- def assert_module_is_not_imported(module_name): +- assert module_name not in sys.modules +- +- @staticmethod +- def assert_module_with_name(module, name): +- assert isinstance(module, types.ModuleType) +- eq_(module.__name__, name) +- +- +-class TestLoadModule(object): +- theory = ImportModuleTheory +- +- def test_load_module__should_fail_for_unknown_module(self): +- assert_raises(ImportError, load_module, "__unknown_module__") +- +- def test_load_module__should_succeed_for_already_imported_module(self): +- module_name = "behave.importer" +- self.theory.assert_module_is_imported(module_name) +- +- module = load_module(module_name) +- self.theory.assert_module_with_name(module, module_name) +- self.theory.assert_module_is_imported(module_name) +- +- def test_load_module__should_succeed_for_existing_module(self): +- module_name = "test._importer_candidate" +- self.theory.ensure_module_is_not_imported(module_name) +- +- module = load_module(module_name) +- self.theory.assert_module_with_name(module, module_name) +- self.theory.assert_module_is_imported(module_name) +- +-class TestLazyObject(object): +- +- def test_get__should_succeed_for_known_object(self): +- lazy = LazyObject("behave.importer", "LazyObject") +- value = lazy.get() +- assert value is LazyObject +- +- lazy2 = LazyObject("behave.importer:LazyObject") +- value2 = lazy2.get() +- assert value2 is LazyObject +- +- lazy3 = LazyObject("behave.formatter.steps", "StepsFormatter") +- value3 = lazy3.get() +- assert issubclass(value3, Formatter) +- +- def test_get__should_fail_for_unknown_module(self): +- lazy = LazyObject("__unknown_module__", "xxx") +- assert_raises(ImportError, lazy.get) +- +- def test_get__should_fail_for_unknown_object_in_module(self): +- lazy = LazyObject("test._importer_candidate", "xxx") +- assert_raises(ImportError, lazy.get) +- +- +-class LazyDictTheory(TestTheory): +- +- @staticmethod +- def safe_getitem(data, key): +- return dict.__getitem__(data, key) +- +- @classmethod +- def assert_item_is_lazy(cls, data, key): +- value = cls.safe_getitem(data, key) +- cls.assert_is_lazy_object(value) +- +- @classmethod +- def assert_item_is_not_lazy(cls, data, key): +- value = cls.safe_getitem(data, key) +- cls.assert_is_not_lazy_object(value) +- +- @staticmethod +- def assert_is_lazy_object(obj): +- assert isinstance(obj, LazyObject) +- +- @staticmethod +- def assert_is_not_lazy_object(obj): +- assert not isinstance(obj, LazyObject) +- +- +-class TestLazyDict(object): +- theory = LazyDictTheory +- +- def test_unknown_item_access__should_raise_keyerror(self): +- lazy_dict = LazyDict({"alice": 42}) +- item_access = lambda key: lazy_dict[key] +- assert_raises(KeyError, item_access, "unknown") +- +- def test_plain_item_access__should_succeed(self): +- theory = LazyDictTheory +- lazy_dict = LazyDict({"alice": 42}) +- theory.assert_item_is_not_lazy(lazy_dict, "alice") +- +- value = lazy_dict["alice"] +- eq_(value, 42) +- +- def test_lazy_item_access__should_load_object(self): +- ImportModuleTheory.ensure_module_is_not_imported("inspect") +- lazy_dict = LazyDict({"alice": LazyObject("inspect:ismodule")}) +- self.theory.assert_item_is_lazy(lazy_dict, "alice") +- self.theory.assert_item_is_lazy(lazy_dict, "alice") +- +- value = lazy_dict["alice"] +- self.theory.assert_is_not_lazy_object(value) +- self.theory.assert_item_is_not_lazy(lazy_dict, "alice") +- +- def test_lazy_item_access__should_fail_with_unknown_module(self): +- lazy_dict = LazyDict({"bob": LazyObject("__unknown_module__", "xxx")}) +- item_access = lambda key: lazy_dict[key] +- assert_raises(ImportError, item_access, "bob") +- +- def test_lazy_item_access__should_fail_with_unknown_object(self): +- lazy_dict = LazyDict({ +- "bob": LazyObject("behave.importer", "XUnknown") +- }) +- item_access = lambda key: lazy_dict[key] +- assert_raises(ImportError, item_access, "bob") +diff --git a/test/_importer_candidate.py b/tests/unit/_importer_candidate.py +similarity index 100% +rename from test/_importer_candidate.py +rename to tests/unit/_importer_candidate.py +diff --git a/tests/unit/reporter/test_summary.py b/tests/unit/reporter/test_summary.py +index d4e85b5..6b947bd 100644 +--- a/tests/unit/reporter/test_summary.py ++++ b/tests/unit/reporter/test_summary.py +@@ -4,8 +4,6 @@ from __future__ import absolute_import, division + import sys + import pytest + from mock import Mock, patch +-# NOT-NEEDED: from nose.tools import * +- + from behave.model import ScenarioOutline, Scenario + from behave.model_core import Status + from behave.reporter.summary import SummaryReporter, format_summary +diff --git a/tests/unit/tag_expression/test_tag_expression_v1_part1.py b/tests/unit/tag_expression/test_tag_expression_v1_part1.py +index 619c710..56fb85d 100644 +--- a/tests/unit/tag_expression/test_tag_expression_v1_part1.py ++++ b/tests/unit/tag_expression/test_tag_expression_v1_part1.py +@@ -2,7 +2,7 @@ + + from __future__ import absolute_import + from behave.tag_expression import TagExpression +-from nose import tools ++import pytest + import unittest + + +@@ -476,31 +476,34 @@ class TestTagExpressionFoo3OrNotBar4AndZap5(unittest.TestCase): + self.e = TagExpression(['foo:3,-bar', 'zap:5']) + + def test_should_count_tags_for_positive_tags(self): +- tools.eq_(self.e.limits, {'foo': 3, 'zap': 5}) ++ assert self.e.limits == {'foo': 3, 'zap': 5} + + def test_should_match_foo_zap(self): + assert self.e.check(['foo', 'zap']) + ++ + class TestTagExpressionParsing(unittest.TestCase): + def setUp(self): + self.e = TagExpression([' foo:3 , -bar ', ' zap:5 ']) + + def test_should_have_limits(self): +- tools.eq_(self.e.limits, {'zap': 5, 'foo': 3}) ++ assert self.e.limits == {'zap': 5, 'foo': 3} ++ + + class TestTagExpressionTagLimits(unittest.TestCase): + def test_should_be_counted_for_negative_tags(self): + e = TagExpression(['-todo:3']) +- tools.eq_(e.limits, {'todo': 3}) ++ assert e.limits == {'todo': 3} + + def test_should_be_counted_for_positive_tags(self): + e = TagExpression(['todo:3']) +- tools.eq_(e.limits, {'todo': 3}) ++ assert e.limits == {'todo': 3} + + def test_should_raise_an_error_for_inconsistent_limits(self): +- tools.assert_raises(Exception, TagExpression, ['todo:3', '-todo:4']) ++ with pytest.raises(Exception): ++ _ = TagExpression(['todo:3', '-todo:4']) + + def test_should_allow_duplicate_consistent_limits(self): + e = TagExpression(['todo:3', '-todo:3']) +- tools.eq_(e.limits, {'todo': 3}) ++ assert e.limits == {'todo': 3} + +diff --git a/tests/unit/tag_expression/test_tag_expression_v1_part2.py b/tests/unit/tag_expression/test_tag_expression_v1_part2.py +index 690235b..cf619da 100644 +--- a/tests/unit/tag_expression/test_tag_expression_v1_part2.py ++++ b/tests/unit/tag_expression/test_tag_expression_v1_part2.py +@@ -6,10 +6,11 @@ REQUIRES: Python >= 2.6, because itertools.combinations() is used. + """ + + from __future__ import absolute_import +-from behave.tag_expression import TagExpression +-from nose import tools + import itertools + from six.moves import range ++import pytest ++from behave.tag_expression import TagExpression ++ + + has_combinations = hasattr(itertools, "combinations") + if has_combinations: +@@ -31,6 +32,7 @@ if has_combinations: + return "@" + " @".join(tags) + return NO_TAGS + ++ + TestCase = object + # ---------------------------------------------------------------------------- + # TEST: all_combinations() test helper +@@ -45,8 +47,8 @@ if has_combinations: + ('@one', '@two'), + ] + actual = all_combinations(items) +- tools.eq_(actual, expected) +- tools.eq_(len(actual), 4) ++ assert actual == expected ++ assert len(actual) == 4 + + def test_all_combinations_with_3values(self): + items = "@one @two @three".split() +@@ -61,8 +63,8 @@ if has_combinations: + ('@one', '@two', '@three'), + ] + actual = all_combinations(items) +- tools.eq_(actual, expected) +- tools.eq_(len(actual), 8) ++ assert actual == expected ++ assert len(actual) == 8 + + + # ---------------------------------------------------------------------------- +@@ -74,13 +76,13 @@ if has_combinations: + tag_combinations, expected): + matched = [ make_tags_line(c) for c in tag_combinations + if tag_expression.check(c) ] +- tools.eq_(matched, expected) ++ assert matched == expected + + def assert_tag_expression_mismatches(self, tag_expression, + tag_combinations, expected): + mismatched = [ make_tags_line(c) for c in tag_combinations + if not tag_expression.check(c) ] +- tools.eq_(mismatched, expected) ++ assert mismatched == expected + + + class TestTagExpressionWith1Term(TagExpressionTestCase): +diff --git a/test/test_ansi_escapes.py b/tests/unit/test_ansi_escapes.py +similarity index 84% +rename from test/test_ansi_escapes.py +rename to tests/unit/test_ansi_escapes.py +index 77564fd..969f3a9 100644 +--- a/test/test_ansi_escapes.py ++++ b/tests/unit/test_ansi_escapes.py +@@ -7,7 +7,7 @@ + # W0621 Redefining name ... from outer scope + + from __future__ import absolute_import +-from nose import tools ++import pytest + from behave.formatter import ansi_escapes + import unittest + from six.moves import range +@@ -44,30 +44,30 @@ class StripEscapesTest(unittest.TestCase): + + def test_should_return_same_text_without_escapes(self): + for text in self.TEXTS: +- tools.eq_(text, ansi_escapes.strip_escapes(text)) ++ assert text == ansi_escapes.strip_escapes(text) + + def test_should_return_empty_string_for_any_ansi_escape(self): + # XXX-JE-CHECK-PY23: If list() is really needed. + for text in list(ansi_escapes.colors.values()): +- tools.eq_("", ansi_escapes.strip_escapes(text)) ++ assert "" == ansi_escapes.strip_escapes(text) + for text in list(ansi_escapes.escapes.values()): +- tools.eq_("", ansi_escapes.strip_escapes(text)) ++ assert "" == ansi_escapes.strip_escapes(text) + + + def test_should_strip_color_escapes_from_text(self): + for text in self.TEXTS: + colored_text = self.colorize_text(text, self.ALL_COLORS) +- tools.eq_(text, ansi_escapes.strip_escapes(colored_text)) ++ assert text == ansi_escapes.strip_escapes(colored_text) + self.assertNotEqual(text, colored_text) + + for color in self.ALL_COLORS: + colored_text = self.colorize(text, color) +- tools.eq_(text, ansi_escapes.strip_escapes(colored_text)) ++ assert text == ansi_escapes.strip_escapes(colored_text) + self.assertNotEqual(text, colored_text) + + def test_should_strip_cursor_up_escapes_from_text(self): + for text in self.TEXTS: + for cursor_up in self.CURSOR_UPS: + colored_text = cursor_up + text + ansi_escapes.escapes["reset"] +- tools.eq_(text, ansi_escapes.strip_escapes(colored_text)) ++ assert text == ansi_escapes.strip_escapes(colored_text) + self.assertNotEqual(text, colored_text) +diff --git a/test/test_configuration.py b/tests/unit/test_configuration.py +similarity index 72% +rename from test/test_configuration.py +rename to tests/unit/test_configuration.py +index e6828e3..c96cf63 100644 +--- a/test/test_configuration.py ++++ b/tests/unit/test_configuration.py +@@ -2,7 +2,7 @@ import os.path + import sys + import tempfile + import six +-from nose.tools import * ++import pytest + from behave import configuration + from behave.configuration import Configuration, UserData + from unittest import TestCase +@@ -36,6 +36,7 @@ if sys.platform.startswith("win"): + ROOTDIR_PREFIX_DEFAULT = ROOTDIR_PREFIX_DEFAULT.lower() + ROOTDIR_PREFIX = os.environ.get("BEHAVE_ROOTDIR_PREFIX", ROOTDIR_PREFIX_DEFAULT) + ++ + class TestConfiguration(object): + + def test_read_file(self): +@@ -46,19 +47,19 @@ class TestConfiguration(object): + + # -- WINDOWS-REQUIRES: normpath + d = configuration.read_configuration(tn) +- eq_(d["outfiles"], [ ++ assert d["outfiles"] ==[ + os.path.normpath(ROOTDIR_PREFIX + "/absolute/path1"), + os.path.normpath(os.path.join(tndir, "relative/path2")), +- ]) +- eq_(d["paths"], [ ++ ] ++ assert d["paths"] == [ + os.path.normpath(ROOTDIR_PREFIX + "/absolute/path3"), + os.path.normpath(os.path.join(tndir, "relative/path4")), +- ]) +- eq_(d["format"], ["pretty", "tag-counter"]) +- eq_(d["default_tags"], ["@foo,~@bar", "@zap"]) +- eq_(d["stdout_capture"], False) +- ok_("bogus" not in d) +- eq_(d["userdata"], {"foo": "bar", "answer": "42"}) ++ ] ++ assert d["format"] == ["pretty", "tag-counter"] ++ assert d["default_tags"] == ["@foo,~@bar", "@zap"] ++ assert d["stdout_capture"] == False ++ assert "bogus" not in d ++ assert d["userdata"] == {"foo": "bar", "answer": "42"} + + def ensure_stage_environment_is_not_set(self): + if "BEHAVE_STAGE" in os.environ: +@@ -69,26 +70,26 @@ class TestConfiguration(object): + self.ensure_stage_environment_is_not_set() + assert "BEHAVE_STAGE" not in os.environ + config = Configuration("") +- eq_("steps", config.steps_dir) +- eq_("environment.py", config.environment_file) ++ assert "steps" == config.steps_dir ++ assert "environment.py" == config.environment_file + + def test_settings_with_stage(self): + config = Configuration(["--stage=STAGE1"]) +- eq_("STAGE1_steps", config.steps_dir) +- eq_("STAGE1_environment.py", config.environment_file) ++ assert "STAGE1_steps" == config.steps_dir ++ assert "STAGE1_environment.py" == config.environment_file + + def test_settings_with_stage_and_envvar(self): + os.environ["BEHAVE_STAGE"] = "STAGE2" + config = Configuration(["--stage=STAGE1"]) +- eq_("STAGE1_steps", config.steps_dir) +- eq_("STAGE1_environment.py", config.environment_file) ++ assert "STAGE1_steps" == config.steps_dir ++ assert "STAGE1_environment.py" == config.environment_file + del os.environ["BEHAVE_STAGE"] + + def test_settings_with_stage_from_envvar(self): + os.environ["BEHAVE_STAGE"] = "STAGE2" + config = Configuration("") +- eq_("STAGE2_steps", config.steps_dir) +- eq_("STAGE2_environment.py", config.environment_file) ++ assert "STAGE2_steps" == config.steps_dir ++ assert "STAGE2_environment.py" == config.environment_file + del os.environ["BEHAVE_STAGE"] + + +@@ -101,33 +102,33 @@ class TestConfigurationUserData(TestCase): + "--define=bar=bar_value", + "--define", "baz=BAZ_VALUE", + ]) +- eq_("foo_value", config.userdata["foo"]) +- eq_("bar_value", config.userdata["bar"]) +- eq_("BAZ_VALUE", config.userdata["baz"]) ++ assert "foo_value" == config.userdata["foo"] ++ assert "bar_value" == config.userdata["bar"] ++ assert "BAZ_VALUE" == config.userdata["baz"] + + def test_cmdline_defines_override_configfile(self): + userdata_init = {"foo": "XXX", "bar": "ZZZ", "baz": 42} + config = Configuration( + "-D foo=foo_value --define bar=123", + load_config=False, userdata=userdata_init) +- eq_("foo_value", config.userdata["foo"]) +- eq_("123", config.userdata["bar"]) +- eq_(42, config.userdata["baz"]) ++ assert "foo_value" == config.userdata["foo"] ++ assert "123" == config.userdata["bar"] ++ assert 42 == config.userdata["baz"] + + def test_cmdline_defines_without_value_are_true(self): + config = Configuration("-D foo --define bar -Dbaz") +- eq_("true", config.userdata["foo"]) +- eq_("true", config.userdata["bar"]) +- eq_("true", config.userdata["baz"]) +- eq_(True, config.userdata.getbool("foo")) ++ assert "true" == config.userdata["foo"] ++ assert "true" == config.userdata["bar"] ++ assert "true" == config.userdata["baz"] ++ assert True == config.userdata.getbool("foo") + + def test_cmdline_defines_with_empty_value(self): + config = Configuration("-D foo=") +- eq_("", config.userdata["foo"]) ++ assert "" == config.userdata["foo"] + + def test_cmdline_defines_with_assign_character_as_value(self): + config = Configuration("-D foo=bar=baz") +- eq_("bar=baz", config.userdata["foo"]) ++ assert "bar=baz" == config.userdata["foo"] + + def test_cmdline_defines__with_quoted_name_value_pair(self): + cmdlines = [ +@@ -136,7 +137,7 @@ class TestConfigurationUserData(TestCase): + ] + for cmdline in cmdlines: + config = Configuration(cmdline, load_config=False) +- eq_(config.userdata, dict(person="Alice and Bob")) ++ assert config.userdata == dict(person="Alice and Bob") + + def test_cmdline_defines__with_quoted_value(self): + cmdlines = [ +@@ -145,7 +146,7 @@ class TestConfigurationUserData(TestCase): + ] + for cmdline in cmdlines: + config = Configuration(cmdline, load_config=False) +- eq_(config.userdata, dict(person="Alice and Bob")) ++ assert config.userdata == dict(person="Alice and Bob") + + def test_setup_userdata(self): + config = Configuration("", load_config=False) +@@ -154,7 +155,7 @@ class TestConfigurationUserData(TestCase): + config.setup_userdata() + + expected_data = dict(person1="Alice", person2="Charly") +- eq_(config.userdata, expected_data) ++ assert config.userdata == expected_data + + def test_update_userdata__with_cmdline_defines(self): + # -- NOTE: cmdline defines are reapplied. +@@ -163,8 +164,8 @@ class TestConfigurationUserData(TestCase): + config.update_userdata(dict(person1="Alice", person2="Bob")) + + expected_data = dict(person1="Alice", person2="Bea", person3="Charly") +- eq_(config.userdata, expected_data) +- eq_(config.userdata_defines, [("person2", "Bea")]) ++ assert config.userdata == expected_data ++ assert config.userdata_defines == [("person2", "Bea")] + + def test_update_userdata__without_cmdline_defines(self): + config = Configuration("", load_config=False) +@@ -172,5 +173,5 @@ class TestConfigurationUserData(TestCase): + config.update_userdata(dict(person1="Alice", person2="Bob")) + + expected_data = dict(person1="Alice", person2="Bob", person3="Charly") +- eq_(config.userdata, expected_data) +- self.assertFalse(config.userdata_defines) ++ assert config.userdata == expected_data ++ assert config.userdata_defines is None +diff --git a/test/test_formatter.py b/tests/unit/test_formatter.py +similarity index 94% +rename from test/test_formatter.py +rename to tests/unit/test_formatter.py +index 42e5f0d..c1a0945 100644 +--- a/test/test_formatter.py ++++ b/tests/unit/test_formatter.py +@@ -6,9 +6,8 @@ import sys + import tempfile + import unittest + import six ++import pytest + from mock import Mock, patch +-from nose.tools import * # pylint: disable=wildcard-import, unused-wildcard-import +- + from behave.formatter._registry import make_formatters + from behave.formatter import pretty + from behave.formatter.base import StreamOpener +@@ -35,7 +34,7 @@ class TestGetTerminalSize(unittest.TestCase): + platform = sys.platform + sys.platform = "windows" + +- eq_(pretty.get_terminal_size(), (80, 24)) ++ assert pretty.get_terminal_size() == (80, 24) + + sys.platform = platform + +@@ -46,7 +45,7 @@ class TestGetTerminalSize(unittest.TestCase): + except ImportError: + pass + +- eq_(pretty.get_terminal_size(), (80, 24)) ++ assert pretty.get_terminal_size() == (80, 24) + + def test_exception_in_ioctl(self): + try: +@@ -59,7 +58,7 @@ class TestGetTerminalSize(unittest.TestCase): + + self.ioctl.side_effect = raiser + +- eq_(pretty.get_terminal_size(), (80, 24)) ++ assert pretty.get_terminal_size() == (80, 24) + self.ioctl.assert_called_with(0, termios.TIOCGWINSZ, self.zero_struct) + + def test_happy_path(self): +@@ -70,7 +69,7 @@ class TestGetTerminalSize(unittest.TestCase): + + self.ioctl.return_value = struct.pack("HHHH", 17, 23, 5, 5) + +- eq_(pretty.get_terminal_size(), (23, 17)) ++ assert pretty.get_terminal_size() == (23, 17) + self.ioctl.assert_called_with(0, termios.TIOCGWINSZ, self.zero_struct) + + def test_zero_size_fallback(self): +@@ -81,7 +80,7 @@ class TestGetTerminalSize(unittest.TestCase): + + self.ioctl.return_value = self.zero_struct + +- eq_(pretty.get_terminal_size(), (80, 24)) ++ assert pretty.get_terminal_size() == (80, 24) + self.ioctl.assert_called_with(0, termios.TIOCGWINSZ, self.zero_struct) + + +@@ -204,7 +203,7 @@ class TestTagsCount(FormatterTests): + p.feature(f) + p.scenario(s) + +- eq_(p.tag_counts, {"ham": [f, s], "spam": [f], "foo": [s]}) ++ assert p.tag_counts == {"ham": [f, s], "spam": [f], "foo": [s]} + + + class MultipleFormattersTests(FormatterTests): +diff --git a/test/test_formatter_progress.py b/tests/unit/test_formatter_progress.py +similarity index 99% +rename from test/test_formatter_progress.py +rename to tests/unit/test_formatter_progress.py +index 29c8e68..19cdf64 100644 +--- a/test/test_formatter_progress.py ++++ b/tests/unit/test_formatter_progress.py +@@ -9,6 +9,7 @@ from __future__ import absolute_import + from .test_formatter import FormatterTests as FormatterTest + from .test_formatter import MultipleFormattersTests as MultipleFormattersTest + ++ + class TestScenarioProgressFormatter(FormatterTest): + formatter_name = "progress" + +@@ -20,20 +21,26 @@ class TestStepProgressFormatter(FormatterTest): + class TestPrettyAndScenarioProgress(MultipleFormattersTest): + formatters = ['pretty', 'progress'] + ++ + class TestPlainAndScenarioProgress(MultipleFormattersTest): + formatters = ['plain', 'progress'] + ++ + class TestJSONAndScenarioProgress(MultipleFormattersTest): + formatters = ['json', 'progress'] + ++ + class TestPrettyAndStepProgress(MultipleFormattersTest): + formatters = ['pretty', 'progress2'] + ++ + class TestPlainAndStepProgress(MultipleFormattersTest): + formatters = ['plain', 'progress2'] + ++ + class TestJSONAndStepProgress(MultipleFormattersTest): + formatters = ['json', 'progress2'] + ++ + class TestScenarioProgressAndStepProgress(MultipleFormattersTest): + formatters = ['progress', 'progress2'] +diff --git a/test/test_formatter_rerun.py b/tests/unit/test_formatter_rerun.py +similarity index 94% +rename from test/test_formatter_rerun.py +rename to tests/unit/test_formatter_rerun.py +index 6357f92..154588f 100644 +--- a/test/test_formatter_rerun.py ++++ b/tests/unit/test_formatter_rerun.py +@@ -8,7 +8,7 @@ from __future__ import absolute_import + from behave.model_core import Status + from .test_formatter import FormatterTests as FormatterTest, _tf + from .test_formatter import MultipleFormattersTests as MultipleFormattersTest +-from nose.tools import * ++ + + class TestRerunFormatter(FormatterTest): + formatter_name = "rerun" +@@ -26,7 +26,7 @@ class TestRerunFormatter(FormatterTest): + p.scenario(scenario) + assert scenario.status == Status.passed + p.eof() +- eq_([], p.failed_scenarios) ++ assert [] == p.failed_scenarios + # -- EMIT REPORT: + p.close() + +@@ -49,7 +49,7 @@ class TestRerunFormatter(FormatterTest): + assert scenarios[0].status == Status.passed + assert scenarios[1].status == Status.failed + p.eof() +- eq_([ failing_scenario ], p.failed_scenarios) ++ assert [ failing_scenario ] == p.failed_scenarios + # -- EMIT REPORT: + p.close() + +@@ -76,7 +76,7 @@ class TestRerunFormatter(FormatterTest): + assert scenarios[1].status == Status.passed + assert scenarios[2].status == Status.failed + p.eof() +- eq_([ failing_scenario1, failing_scenario2 ], p.failed_scenarios) ++ assert [ failing_scenario1, failing_scenario2 ] == p.failed_scenarios + # -- EMIT REPORT: + p.close() + +@@ -84,14 +84,18 @@ class TestRerunFormatter(FormatterTest): + class TestRerunAndPrettyFormatters(MultipleFormattersTest): + formatters = ["rerun", "pretty"] + ++ + class TestRerunAndPlainFormatters(MultipleFormattersTest): + formatters = ["rerun", "plain"] + ++ + class TestRerunAndScenarioProgressFormatters(MultipleFormattersTest): + formatters = ["rerun", "progress"] + ++ + class TestRerunAndStepProgressFormatters(MultipleFormattersTest): + formatters = ["rerun", "progress2"] + ++ + class TestRerunAndJsonFormatter(MultipleFormattersTest): + formatters = ["rerun", "json"] +diff --git a/test/test_formatter_tags.py b/tests/unit/test_formatter_tags.py +similarity index 99% +rename from test/test_formatter_tags.py +rename to tests/unit/test_formatter_tags.py +index 5125d51..9f95374 100644 +--- a/test/test_formatter_tags.py ++++ b/tests/unit/test_formatter_tags.py +@@ -9,12 +9,14 @@ from __future__ import absolute_import + from .test_formatter import FormatterTests as FormatterTest + from .test_formatter import MultipleFormattersTests as MultipleFormattersTest + ++ + # ----------------------------------------------------------------------------- + # FORMATTER TESTS: With TagCountFormatter + # ----------------------------------------------------------------------------- + class TestTagsCountFormatter(FormatterTest): + formatter_name = "tags" + ++ + # ----------------------------------------------------------------------------- + # FORMATTER TESTS: With TagLocationFormatter + # ----------------------------------------------------------------------------- +@@ -28,12 +30,15 @@ class TestTagsLocationFormatter(FormatterTest): + class TestPrettyAndTagsCount(MultipleFormattersTest): + formatters = ["pretty", "tags"] + ++ + class TestPlainAndTagsCount(MultipleFormattersTest): + formatters = ["plain", "tags"] + ++ + class TestJSONAndTagsCount(MultipleFormattersTest): + formatters = ["json", "tags"] + ++ + class TestRerunAndTagsCount(MultipleFormattersTest): + formatters = ["rerun", "tags"] + +@@ -44,14 +49,18 @@ class TestRerunAndTagsCount(MultipleFormattersTest): + class TestPrettyAndTagsLocation(MultipleFormattersTest): + formatters = ["pretty", "tags.location"] + ++ + class TestPlainAndTagsLocation(MultipleFormattersTest): + formatters = ["plain", "tags.location"] + ++ + class TestJSONAndTagsLocation(MultipleFormattersTest): + formatters = ["json", "tags.location"] + ++ + class TestRerunAndTagsLocation(MultipleFormattersTest): + formatters = ["rerun", "tags.location"] + ++ + class TestTagsCountAndTagsLocation(MultipleFormattersTest): + formatters = ["tags", "tags.location"] +diff --git a/tests/unit/test_importer.py b/tests/unit/test_importer.py +index f3f4e2c..055b9fb 100644 +--- a/tests/unit/test_importer.py ++++ b/tests/unit/test_importer.py +@@ -62,7 +62,7 @@ class TestLoadModule(object): + self.theory.assert_module_is_imported(module_name) + + def test_load_module__should_succeed_for_existing_module(self): +- module_name = "test._importer_candidate" ++ module_name = "tests.unit._importer_candidate" + self.theory.ensure_module_is_not_imported(module_name) + + module = load_module(module_name) +diff --git a/test/test_log_capture.py b/tests/unit/test_log_capture.py +similarity index 87% +rename from test/test_log_capture.py +rename to tests/unit/test_log_capture.py +index bfdbed7..bf1874c 100644 +--- a/test/test_log_capture.py ++++ b/tests/unit/test_log_capture.py +@@ -1,11 +1,10 @@ + from __future__ import absolute_import, with_statement +- +-from nose.tools import * ++import pytest + from mock import patch +- + from behave.log_capture import LoggingCapture + from six.moves import range + ++ + class TestLogCapture(object): + def test_get_value_returns_all_log_records(self): + class FakeConfig(object): +@@ -23,7 +22,7 @@ class TestLogCapture(object): + format.return_value = 'foo' + expected = '\n'.join(['foo'] * len(fake_records)) + +- eq_(handler.getvalue(), expected) ++ assert handler.getvalue() == expected + + calls = [args[0][0] for args in format.call_args_list] +- eq_(calls, fake_records) ++ assert calls == fake_records +diff --git a/test/test_matchers.py b/tests/unit/test_matchers.py +similarity index 93% +rename from test/test_matchers.py +rename to tests/unit/test_matchers.py +index bfe04fc..815581c 100644 +--- a/test/test_matchers.py ++++ b/tests/unit/test_matchers.py +@@ -1,7 +1,7 @@ + # -*- coding: UTF-8 -*- + from __future__ import absolute_import, with_statement ++import pytest + from mock import Mock, patch +-from nose.tools import * # pylint: disable=wildcard-import, unused-wildcard-import + import parse + from behave.matchers import Match, Matcher, ParseMatcher, RegexMatcher, \ + SimplifiedRegexMatcher, CucumberRegexMatcher +@@ -80,7 +80,7 @@ class TestParseMatcher(object): + assert m.func is func + args = m.arguments + have = [(a.start, a.end, a.original, a.value, a.name) for a in args] +- eq_(have, expected) ++ assert have == expected + + def test_named_arguments(self): + text = "has a {string}, an {integer:d} and a {decimal:f}" +@@ -89,11 +89,11 @@ class TestParseMatcher(object): + + m = matcher.match("has a foo, an 11 and a 3.14159") + m.run(context) +- eq_(self.recorded_args, ((context,), { ++ assert self.recorded_args, ((context,) == { + 'string': 'foo', + 'integer': 11, + 'decimal': 3.14159 +- })) ++ }) + + def test_positional_arguments(self): + text = "has a {}, an {:d} and a {:f}" +@@ -102,7 +102,7 @@ class TestParseMatcher(object): + + m = matcher.match("has a foo, an 11 and a 3.14159") + m.run(context) +- eq_(self.recorded_args, ((context, 'foo', 11, 3.14159), {})) ++ assert self.recorded_args == ((context, 'foo', 11, 3.14159), {}) + + class TestRegexMatcher(object): + # pylint: disable=invalid-name, no-self-use +@@ -151,7 +151,7 @@ class TestRegexMatcher(object): + assert m.func is func + args = m.arguments + have = [(a.start, a.end, a.original, a.value, a.name) for a in args] +- eq_(have, expected) ++ assert have == expected + + + +@@ -179,17 +179,17 @@ class TestSimplifiedRegexMatcher(TestRegexMatcher): + assert isinstance(matched1, Match) + assert isinstance(matched2, Match) + +- @raises(AssertionError) + def test_step_should_not_use_regex_begin_marker(self): +- SimplifiedRegexMatcher(None, "^I do something") ++ with pytest.raises(AssertionError): ++ SimplifiedRegexMatcher(None, "^I do something") + +- @raises(AssertionError) + def test_step_should_not_use_regex_end_marker(self): +- SimplifiedRegexMatcher(None, "I do something$") ++ with pytest.raises(AssertionError): ++ SimplifiedRegexMatcher(None, "I do something$") + +- @raises(AssertionError) + def test_step_should_not_use_regex_begin_and_end_marker(self): +- SimplifiedRegexMatcher(None, "^I do something$") ++ with pytest.raises(AssertionError): ++ SimplifiedRegexMatcher(None, "^I do something$") + + + class TestCucumberRegexMatcher(TestRegexMatcher): +diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py +index 5603a4b..ecbb1bf 100644 +--- a/tests/unit/test_parser.py ++++ b/tests/unit/test_parser.py +@@ -7,7 +7,6 @@ Unit tests for Gherkin parser: :mod:`behave.parser`. + from __future__ import absolute_import, print_function + import pytest + from behave import i18n, model, parser +-# NOT-NEEDED: from nose.tools import * + + + # --------------------------------------------------------------------------- +diff --git a/test/test_runner.py b/tests/unit/test_runner.py +similarity index 82% +rename from test/test_runner.py +rename to tests/unit/test_runner.py +index 6647283..030dffa 100644 +--- a/test/test_runner.py ++++ b/tests/unit/test_runner.py +@@ -11,10 +11,8 @@ import tempfile + import unittest + import six + from six import StringIO +- ++import pytest + from mock import Mock, patch +-from nose.tools import * # pylint: disable=wildcard-import, unused-wildcard-import +- + from behave import runner_util + from behave.model import Table + from behave.step_registry import StepRegistry +@@ -39,31 +37,31 @@ class TestContext(unittest.TestCase): + def test_user_mode_shall_restore_behave_mode(self): + # -- CASE: No exception is raised. + initial_mode = runner.Context.BEHAVE +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + with self.context.use_with_user_mode(): +- eq_(self.context._mode, runner.Context.USER) ++ assert self.context._mode == runner.Context.USER + self.context.thing = "stuff" +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_user_mode_shall_restore_behave_mode_if_assert_fails(self): + initial_mode = runner.Context.BEHAVE +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + try: + with self.context.use_with_user_mode(): +- eq_(self.context._mode, runner.Context.USER) ++ assert self.context._mode == runner.Context.USER + assert False, "XFAIL" + except AssertionError: +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_user_mode_shall_restore_behave_mode_if_exception_is_raised(self): + initial_mode = runner.Context.BEHAVE +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + try: + with self.context.use_with_user_mode(): +- eq_(self.context._mode, runner.Context.USER) ++ assert self.context._mode == runner.Context.USER + raise RuntimeError("XFAIL") + except RuntimeError: +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_use_with_user_mode__shall_restore_initial_mode(self): + # -- CASE: No exception is raised. +@@ -71,9 +69,9 @@ class TestContext(unittest.TestCase): + initial_mode = runner.Context.BEHAVE + self.context._mode = initial_mode + with self.context.use_with_user_mode(): +- eq_(self.context._mode, runner.Context.USER) ++ assert self.context._mode == runner.Context.USER + self.context.thing = "stuff" +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_use_with_user_mode__shall_restore_initial_mode_with_error(self): + # -- CASE: Exception is raised. +@@ -82,10 +80,10 @@ class TestContext(unittest.TestCase): + self.context._mode = initial_mode + try: + with self.context.use_with_user_mode(): +- eq_(self.context._mode, runner.Context.USER) ++ assert self.context._mode == runner.Context.USER + raise RuntimeError("XFAIL") + except RuntimeError: +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_use_with_behave_mode__shall_restore_initial_mode(self): + # -- CASE: No exception is raised. +@@ -93,9 +91,9 @@ class TestContext(unittest.TestCase): + initial_mode = runner.Context.USER + self.context._mode = initial_mode + with self.context._use_with_behave_mode(): +- eq_(self.context._mode, runner.Context.BEHAVE) ++ assert self.context._mode == runner.Context.BEHAVE + self.context.thing = "stuff" +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_use_with_behave_mode__shall_restore_initial_mode_with_error(self): + # -- CASE: Exception is raised. +@@ -104,22 +102,22 @@ class TestContext(unittest.TestCase): + self.context._mode = initial_mode + try: + with self.context._use_with_behave_mode(): +- eq_(self.context._mode, runner.Context.BEHAVE) ++ assert self.context._mode == runner.Context.BEHAVE + raise RuntimeError("XFAIL") + except RuntimeError: +- eq_(self.context._mode, initial_mode) ++ assert self.context._mode == initial_mode + + def test_context_contains(self): +- eq_("thing" in self.context, False) ++ assert "thing" not in self.context + self.context.thing = "stuff" +- eq_("thing" in self.context, True) ++ assert "thing" in self.context + self.context._push() +- eq_("thing" in self.context, True) ++ assert "thing" in self.context + + def test_attribute_set_at_upper_level_visible_at_lower_level(self): + self.context.thing = "stuff" + self.context._push() +- eq_(self.context.thing, "stuff") ++ assert self.context.thing == "stuff" + + def test_attribute_set_at_lower_level_not_visible_at_upper_level(self): + self.context._push() +@@ -130,16 +128,16 @@ class TestContext(unittest.TestCase): + def test_attributes_set_at_upper_level_visible_at_lower_level(self): + self.context.thing = "stuff" + self.context._push() +- eq_(self.context.thing, "stuff") ++ assert self.context.thing == "stuff" + self.context.other_thing = "more stuff" + self.context._push() +- eq_(self.context.thing, "stuff") +- eq_(self.context.other_thing, "more stuff") ++ assert self.context.thing == "stuff" ++ assert self.context.other_thing == "more stuff" + self.context.third_thing = "wombats" + self.context._push() +- eq_(self.context.thing, "stuff") +- eq_(self.context.other_thing, "more stuff") +- eq_(self.context.third_thing, "wombats") ++ assert self.context.thing == "stuff" ++ assert self.context.other_thing == "more stuff" ++ assert self.context.third_thing == "wombats" + + def test_attributes_set_at_lower_level_not_visible_at_upper_level(self): + self.context.thing = "stuff" +@@ -149,17 +147,17 @@ class TestContext(unittest.TestCase): + + self.context._push() + self.context.third_thing = "wombats" +- eq_(self.context.thing, "stuff") +- eq_(self.context.other_thing, "more stuff") +- eq_(self.context.third_thing, "wombats") ++ assert self.context.thing == "stuff" ++ assert self.context.other_thing == "more stuff" ++ assert self.context.third_thing == "wombats" + + self.context._pop() +- eq_(self.context.thing, "stuff") +- eq_(self.context.other_thing, "more stuff") ++ assert self.context.thing == "stuff" ++ assert self.context.other_thing == "more stuff" + assert getattr(self.context, "third_thing", None) is None, "%s is not None" % self.context.third_thing + + self.context._pop() +- eq_(self.context.thing, "stuff") ++ assert self.context.thing == "stuff" + assert getattr(self.context, "other_thing", None) is None, "%s is not None" % self.context.other_thing + assert getattr(self.context, "third_thing", None) is None, "%s is not None" % self.context.third_thing + +@@ -270,21 +268,22 @@ class TestContext(unittest.TestCase): + assert filename in info, "%r not in %r" % (filename, info) + + def test_context_deletable(self): +- eq_("thing" in self.context, False) ++ assert "thing" not in self.context + self.context.thing = "stuff" +- eq_("thing" in self.context, True) ++ assert "thing" in self.context + del self.context.thing +- eq_("thing" in self.context, False) ++ assert "thing" not in self.context + +- @raises(AttributeError) ++ # OLD: @raises(AttributeError) + def test_context_deletable_raises(self): + # pylint: disable=protected-access +- eq_("thing" in self.context, False) ++ assert "thing" not in self.context + self.context.thing = "stuff" +- eq_("thing" in self.context, True) ++ assert "thing" in self.context + self.context._push() +- eq_("thing" in self.context, True) +- del self.context.thing ++ assert "thing" in self.context ++ with pytest.raises(AttributeError): ++ del self.context.thing + + + class ExampleSteps(object): +@@ -362,7 +361,7 @@ Then a step passes + """.lstrip() + with patch("behave.step_registry.registry", self.step_registry): + result = self.context.execute_steps(doc) +- eq_(result, True) ++ assert result is True + + def test_execute_steps_with_failing_step(self): + doc = u""" +@@ -374,7 +373,7 @@ Then a step passes + try: + result = self.context.execute_steps(doc) + except AssertionError as e: +- ok_("FAILED SUB-STEP: When a step fails" in _text(e)) ++ assert "FAILED SUB-STEP: When a step fails" in _text(e) + + def test_execute_steps_with_undefined_step(self): + doc = u""" +@@ -386,7 +385,7 @@ Then a step passes + try: + result = self.context.execute_steps(doc) + except AssertionError as e: +- ok_("UNDEFINED SUB-STEP: When a step is undefined" in _text(e)) ++ assert "UNDEFINED SUB-STEP: When a step is undefined" in _text(e) + + def test_execute_steps_with_text(self): + doc = u''' +@@ -401,8 +400,8 @@ Then a step passes + with patch("behave.step_registry.registry", self.step_registry): + result = self.context.execute_steps(doc) + expected_text = "Lorem ipsum\nIpsum lorem" +- eq_(result, True) +- eq_(expected_text, ExampleSteps.text) ++ assert result is True ++ assert expected_text == ExampleSteps.text + + def test_execute_steps_with_table(self): + doc = u""" +@@ -419,8 +418,8 @@ Then a step passes + [u"Alice", u"12"], + [u"Bob", u"23"], + ]) +- eq_(result, True) +- eq_(expected_table, ExampleSteps.table) ++ assert result is True ++ assert expected_table == ExampleSteps.table + + def test_context_table_is_restored_after_execute_steps_without_table(self): + doc = u""" +@@ -431,7 +430,7 @@ Then a step passes + original_table = "" + self.context.table = original_table + self.context.execute_steps(doc) +- eq_(self.context.table, original_table) ++ assert self.context.table == original_table + + def test_context_table_is_restored_after_execute_steps_with_table(self): + doc = u""" +@@ -445,7 +444,7 @@ Then a step passes + original_table = "" + self.context.table = original_table + self.context.execute_steps(doc) +- eq_(self.context.table, original_table) ++ assert self.context.table == original_table + + def test_context_text_is_restored_after_execute_steps_without_text(self): + doc = u""" +@@ -456,7 +455,7 @@ Then a step passes + original_text = "" + self.context.text = original_text + self.context.execute_steps(doc) +- eq_(self.context.text, original_text) ++ assert self.context.text == original_text + + def test_context_text_is_restored_after_execute_steps_with_text(self): + doc = u''' +@@ -471,10 +470,10 @@ When a step with text: + original_text = "" + self.context.text = original_text + self.context.execute_steps(doc) +- eq_(self.context.text, original_text) ++ assert self.context.text == original_text + + +- @raises(ValueError) ++ # OLD: @raises(ValueError) + def test_execute_steps_should_fail_when_called_without_feature(self): + doc = u""" + Given a passes +@@ -482,7 +481,8 @@ Then a step passes + """.lstrip() + with patch("behave.step_registry.registry", self.step_registry): + self.context.feature = None +- self.context.execute_steps(doc) ++ with pytest.raises(ValueError): ++ self.context.execute_steps(doc) + + + def create_mock_config(): +@@ -588,11 +588,11 @@ class TestRunner(object): + r.setup_capture() + r.start_capture() + +- eq_(sys.stdout, r.capture_controller.stdout_capture) ++ assert sys.stdout == r.capture_controller.stdout_capture + + r.stop_capture() + +- eq_(sys.stdout, new_stdout) ++ assert sys.stdout == new_stdout + + sys.stdout = old_stdout + +@@ -605,11 +605,11 @@ class TestRunner(object): + + r.start_capture() + +- eq_(sys.stdout, old_stdout) ++ assert sys.stdout == old_stdout + + r.stop_capture() + +- eq_(sys.stdout, old_stdout) ++ assert sys.stdout == old_stdout + + def test_teardown_capture_removes_log_tap(self): + r = runner.Runner(Mock()) +@@ -633,7 +633,7 @@ class TestRunner(object): + # pylint: disable=too-many-format-args + assert "spam" in l, '"spam" variable not set in locals (%r)' % (g, l) + # pylint: enable=too-many-format-args +- eq_(l["spam"], fn) ++ assert l["spam"] == fn + + def test_run_returns_true_if_everything_passed(self): + r = runner.Runner(Mock()) +@@ -694,11 +694,11 @@ class TestRunWithPaths(unittest.TestCase): + self.runner.context = Mock() + self.runner.run_with_paths() + +- eq_(self.run_hook.call_args_list, [ ++ assert self.run_hook.call_args_list == [ + ((), {}), + (("before_all", self.runner.context), {}), + (("after_all", self.runner.context), {}), +- ]) ++ ] + + @patch("behave.parser.parse_file") + @patch("os.path.abspath") +@@ -724,8 +724,8 @@ class TestRunWithPaths(unittest.TestCase): + + expected_parse_file_args = \ + [((x.upper(),), {"language": "fritz"}) for x in feature_locations] +- eq_(parse_file.call_args_list, expected_parse_file_args) +- eq_(self.runner.features, [feature] * 3) ++ assert parse_file.call_args_list == expected_parse_file_args ++ assert self.runner.features == [feature] * 3 + + + class FsMock(object): +@@ -829,9 +829,12 @@ class TestFeatureDirectory(object): + + # will look for a "features" directory and not find one + with patch("os.path", fs): +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + +- ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls ++ # ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) + + def test_default_path_no_features(self): + config = create_mock_config() +@@ -842,7 +845,9 @@ class TestFeatureDirectory(object): + fs = FsMock("features/steps/") + with patch("os.path", fs): + with patch("os.walk", fs.walk): +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + + def test_default_path(self): + config = create_mock_config() +@@ -857,7 +862,7 @@ class TestFeatureDirectory(object): + with r.path_manager: + r.setup_paths() + +- eq_(r.base_dir, os.path.abspath("features")) ++ assert r.base_dir == os.path.abspath("features") + + def test_supplied_feature_file(self): + config = create_mock_config() +@@ -872,10 +877,12 @@ class TestFeatureDirectory(object): + with patch("os.walk", fs.walk): + with r.path_manager: + r.setup_paths() +- ok_(("isdir", os.path.join(fs.base, "steps")) in fs.calls) +- ok_(("isfile", os.path.join(fs.base, "foo.feature")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "steps")) in fs.calls ++ assert ("isfile", os.path.join(fs.base, "foo.feature")) in fs.calls ++ # OLD: ok_(("isdir", os.path.join(fs.base, "steps")) in fs.calls) ++ # OLD: ok_(("isfile", os.path.join(fs.base, "foo.feature")) in fs.calls) + +- eq_(r.base_dir, fs.base) ++ assert r.base_dir == fs.base + + def test_supplied_feature_file_no_steps(self): + config = create_mock_config() +@@ -888,7 +895,9 @@ class TestFeatureDirectory(object): + with patch("os.path", fs): + with patch("os.walk", fs.walk): + with r.path_manager: +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + + def test_supplied_feature_directory(self): + config = create_mock_config() +@@ -903,9 +912,10 @@ class TestFeatureDirectory(object): + with r.path_manager: + r.setup_paths() + +- ok_(("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls ++ # OLD ok_(("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls) + +- eq_(r.base_dir, os.path.join(fs.base, "spam")) ++ assert r.base_dir == os.path.join(fs.base, "spam") + + def test_supplied_feature_directory_no_steps(self): + config = create_mock_config() +@@ -917,9 +927,12 @@ class TestFeatureDirectory(object): + + with patch("os.path", fs): + with patch("os.walk", fs.walk): +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + +- ok_(("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls ++ # OLD: ok_(("isdir", os.path.join(fs.base, "spam", "steps")) in fs.calls) + + def test_supplied_feature_directory_missing(self): + config = create_mock_config() +@@ -931,7 +944,9 @@ class TestFeatureDirectory(object): + + with patch("os.path", fs): + with patch("os.walk", fs.walk): +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + + + class TestFeatureDirectoryLayout2(object): +@@ -955,7 +970,7 @@ class TestFeatureDirectoryLayout2(object): + with r.path_manager: + r.setup_paths() + +- eq_(r.base_dir, os.path.abspath("features")) ++ assert r.base_dir == os.path.abspath("features") + + def test_supplied_root_directory(self): + config = create_mock_config() +@@ -975,8 +990,9 @@ class TestFeatureDirectoryLayout2(object): + with r.path_manager: + r.setup_paths() + +- ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) +- eq_(r.base_dir, os.path.join(fs.base, "features")) ++ # OLD: ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls ++ assert r.base_dir == os.path.join(fs.base, "features") + + def test_supplied_root_directory_no_steps(self): + config = create_mock_config() +@@ -993,10 +1009,13 @@ class TestFeatureDirectoryLayout2(object): + with patch("os.path", fs): + with patch("os.walk", fs.walk): + with r.path_manager: +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + +- ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) +- eq_(r.base_dir, None) ++ # OLD: ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls ++ assert r.base_dir is None + + + def test_supplied_feature_file(self): +@@ -1018,9 +1037,11 @@ class TestFeatureDirectoryLayout2(object): + with r.path_manager: + r.setup_paths() + +- ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) +- ok_(("isfile", os.path.join(fs.base, "features", "group1", "foo.feature")) in fs.calls) +- eq_(r.base_dir, fs.join(fs.base, "features")) ++ # OLD: ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ # OLD: ok_(("isfile", os.path.join(fs.base, "features", "group1", "foo.feature")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls ++ assert ("isfile", os.path.join(fs.base, "features", "group1", "foo.feature")) in fs.calls ++ assert r.base_dir == fs.join(fs.base, "features") + + def test_supplied_feature_file_no_steps(self): + config = create_mock_config() +@@ -1037,7 +1058,9 @@ class TestFeatureDirectoryLayout2(object): + with patch("os.path", fs): + with patch("os.walk", fs.walk): + with r.path_manager: +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD assert_raises(ConfigError, r.setup_paths) + + def test_supplied_feature_directory(self): + config = create_mock_config() +@@ -1057,8 +1080,9 @@ class TestFeatureDirectoryLayout2(object): + with r.path_manager: + r.setup_paths() + +- ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) +- eq_(r.base_dir, os.path.join(fs.base, "features")) ++ # OLD: ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls ++ assert r.base_dir == os.path.join(fs.base, "features") + + + def test_supplied_feature_directory_no_steps(self): +@@ -1075,6 +1099,9 @@ class TestFeatureDirectoryLayout2(object): + + with patch("os.path", fs): + with patch("os.walk", fs.walk): +- assert_raises(ConfigError, r.setup_paths) ++ with pytest.raises(ConfigError): ++ r.setup_paths() ++ # OLD: assert_raises(ConfigError, r.setup_paths) + +- ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ # OLD: ok_(("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls) ++ assert ("isdir", os.path.join(fs.base, "features", "steps")) in fs.calls +diff --git a/test/test_step_registry.py b/tests/unit/test_step_registry.py +similarity index 95% +rename from test/test_step_registry.py +rename to tests/unit/test_step_registry.py +index f5b5a4d..6f85729 100644 +--- a/test/test_step_registry.py ++++ b/tests/unit/test_step_registry.py +@@ -2,7 +2,6 @@ + # pylint: disable=unused-wildcard-import + from __future__ import absolute_import, with_statement + from mock import Mock, patch +-from nose.tools import * # pylint: disable=wildcard-import + from six.moves import range # pylint: disable=redefined-builtin + from behave import step_registry + +@@ -26,7 +25,7 @@ class TestStepRegistry(object): + + registry.add_step_definition(step_type.upper(), pattern, func) + get_matcher.assert_called_with(func, pattern) +- eq_(l, [magic_object]) ++ assert l == [magic_object] + + def test_find_match_with_specific_step_type_also_searches_generic(self): + registry = step_registry.StepRegistry() +@@ -80,7 +79,7 @@ class TestStepRegistry(object): + + assert registry.find_match(step) is magic_object + for mock in step_defs[6:]: +- eq_(mock.match.call_count, 0) ++ assert mock.match.call_count == 0 + + # pylint: disable=line-too-long + @patch.object(step_registry.registry, 'add_step_definition') +diff --git a/tests/unit/test_textutil.py b/tests/unit/test_textutil.py +index e05e9ad..3ffab3c 100644 +--- a/tests/unit/test_textutil.py ++++ b/tests/unit/test_textutil.py +@@ -9,6 +9,10 @@ import pytest + import codecs + import six + ++ ++pytest_version = pytest.__version__ ++ ++ + # ----------------------------------------------------------------------------- + # TEST SUPPORT: + # ----------------------------------------------------------------------------- +@@ -263,6 +267,7 @@ class TestObjectToTextConversion(object): + assert message in text2, "OOPS: text=%r" % text2 + + @requires_python2 ++ @pytest.mark.skipif(pytest_version >= "5.0", reason="Fails with pytest 5.0") + @pytest.mark.parametrize("exception_class, message", [ + (AssertionError, u"Ärgernis"), + (RuntimeError, u"Übermütig"), +@@ -274,10 +279,15 @@ class TestObjectToTextConversion(object): + with pytest.raises(exception_class) as e: + raise exception_class(bytes_message) + +- text2 = text(e.value) ++ # -- REQUIRES: pytest < 5.0 ++ # HINT: pytest >= 5.0 needs: text(e.value) ++ # NEW: text2 = text(e.value) # Causes problems w/ decoding and comparison. ++ assert isinstance(e.value, Exception) ++ text2 = text(e) + unicode_message = bytes_message.decode(self.ENCODING) + expected = u"%s: %s" % (exception_class.__name__, unicode_message) + assert isinstance(text2, six.text_type) ++ assert unicode_message in text2 + assert text2.endswith(expected) + # -- DIAGNOSTICS: + print(u"text2: "+ text2) +diff --git a/tox.ini b/tox.ini +index 392bb39..d2fbce2 100644 +--- a/tox.ini ++++ b/tox.ini +@@ -67,17 +67,11 @@ deps= + install_command = pip install -U {opts} {packages} + changedir = {toxinidir} + commands= +- pytest {posargs:test tests} ++ pytest {posargs:tests} + behave --format=progress {posargs:features} + behave --format=progress {posargs:tools/test-features} + behave --format=progress {posargs:issue.features} +-deps= +- pytest>=3.0 +- pytest-html >= 1.19.0 +- nose>=1.3 +- mock>=2.0 +- PyHamcrest>=1.9 +- path.py >= 10.1 ++deps= -r {toxinidir}/py.requirements/ci.tox.txt + setenv = + PYTHONPATH = {toxinidir} + +@@ -97,13 +91,12 @@ changedir = {envdir} + commands= + behave --version + {toxinidir}/bin/toxcmd.py copytree ../../behave4cmd0 . +- {toxinidir}/bin/toxcmd.py copytree ../../test . + {toxinidir}/bin/toxcmd.py copytree ../../tests . + {toxinidir}/bin/toxcmd.py copytree ../../features . + {toxinidir}/bin/toxcmd.py copytree ../../tools . + {toxinidir}/bin/toxcmd.py copytree ../../issue.features . + {toxinidir}/bin/toxcmd.py copy ../../behave.ini . +- pytest {posargs:test tests} ++ pytest {posargs:tests} + behave --format=progress {posargs:features} + behave --format=progress {posargs:tools/test-features} + behave --format=progress {posargs:issue.features} +@@ -119,18 +112,16 @@ changedir = {envdir} + commands= + behave --version + {toxinidir}/bin/toxcmd.py copytree ../../behave4cmd0 . +- {toxinidir}/bin/toxcmd.py copytree ../../test . + {toxinidir}/bin/toxcmd.py copytree ../../tests . + {toxinidir}/bin/toxcmd.py copytree ../../features . + {toxinidir}/bin/toxcmd.py copytree ../../tools . + {toxinidir}/bin/toxcmd.py copytree ../../issue.features . + {toxinidir}/bin/toxcmd.py copy ../../behave.ini . + {toxinidir}/bin/toxcmd.py 2to3 -w -n --no-diffs behave4cmd0 +- {toxinidir}/bin/toxcmd.py 2to3 -w -n --no-diffs test + {toxinidir}/bin/toxcmd.py 2to3 -w -n --no-diffs tools + {toxinidir}/bin/toxcmd.py 2to3 -w -n --no-diffs features + {toxinidir}/bin/toxcmd.py 2to3 -w -n --no-diffs issue.features +- pytest {posargs:test tests} ++ pytest {posargs:tests} + behave --format=progress {posargs:features} + behave --format=progress {posargs:tools/test-features} + behave --format=progress {posargs:issue.features} +@@ -148,7 +139,7 @@ setenv = + [testenv:jy27] + basepython= jython + commands= +- pytest {posargs:test tests} ++ pytest {posargs:tests} + behave --format=progress {posargs:features} + behave --format=progress {posargs:tools/test-features} + behave --format=progress {posargs:issue.features} diff --git a/meta-python/recipes-devtools/python/python3-behave/0036-FIX-Remove-test-from-pytest-run.patch b/meta-python/recipes-devtools/python/python3-behave/0036-FIX-Remove-test-from-pytest-run.patch new file mode 100644 index 000000000..7fe451b2f --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0036-FIX-Remove-test-from-pytest-run.patch @@ -0,0 +1,22 @@ +From 2c886d201c19085ab52065d47e4f86a82a77f991 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 09:17:54 +0200 +Subject: [PATCH] FIX: Remove test/ from pytest run. + +--- + .travis.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.travis.yml b/.travis.yml +index fbc3520..c6027e0 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -35,7 +35,7 @@ install: + + script: + - python --version +- - pytest test tests ++ - pytest tests + - behave -f progress --junit features/ + - behave -f progress --junit tools/test-features/ + - behave -f progress --junit issue.features/ diff --git a/meta-python/recipes-devtools/python/python3-behave/0037-Select-by-location-Add-support-for-Scenario-containe.patch b/meta-python/recipes-devtools/python/python3-behave/0037-Select-by-location-Add-support-for-Scenario-containe.patch new file mode 100644 index 000000000..3e9a46a0c --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0037-Select-by-location-Add-support-for-Scenario-containe.patch @@ -0,0 +1,652 @@ +From 57a9cc0e1c99b0ed2ec5dbfd3fcfce456cfe13c8 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 09:50:46 +0200 +Subject: [PATCH] Select-by-location: Add support for "Scenario container" + (Feature, Rule, ScenarioOutline) (related to: #391) + +--- + CHANGES.rst | 2 +- + behave/model.py | 49 +++-- + behave/runner_util.py | 186 +++++++++++++++++- + ....select_scenarios_by_file_location.feature | 27 ++- + pytest.ini | 2 +- + tests/unit/test_runner_util.py | 175 ++++++++++++++++ + 6 files changed, 416 insertions(+), 25 deletions(-) + create mode 100644 tests/unit/test_runner_util.py + +diff --git a/CHANGES.rst b/CHANGES.rst +index 3d805b3..01bd1bd 100644 +--- a/CHANGES.rst ++++ b/CHANGES.rst +@@ -28,7 +28,7 @@ ENHANCEMENTS: + * Use `cucumber-tag-expressions`_ with tag-matching extension (superceeds: old-style tag-expressions) + * issue #678: Scenario Outline: Support tags with commas and semicolons (provided by: lawnmowerlatte, pull #679) + * issue #675: Feature files cannot be found within symlink directories (provided by: smadness, pull #680) +- ++* Select-by-location: Add support for "Scenario container" (Feature, Rule, ScenarioOutline) (related to: #391) + + PARTIALLY FIXED: + +diff --git a/behave/model.py b/behave/model.py +index 3084850..7fc534a 100644 +--- a/behave/model.py ++++ b/behave/model.py +@@ -55,11 +55,11 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + .. attribute:: keyword + + This is the keyword as seen in the *feature file*. In English this will +- be "Feature". ++ be "Feature" or "Rule". + + .. attribute:: name + +- The name of the feature (the text after "Feature".) ++ The name (or title) of the model entity (the text after the keyword.) + + .. attribute:: description + +@@ -93,12 +93,16 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + + Status.untested + The feature was has not been completely tested yet. ++ + Status.skipped +- One or more steps of this feature was passed over during testing. ++ The execution of this model entity (feature or rule) ++ should be / was skipped (excluded from the test run). ++ + Status.passed +- The feature was tested successfully. ++ The model entity (feature or rule) was tested successfully. ++ + Status.failed +- One or more steps of this feature failed. ++ One or more run items of this model entity failed. + + .. versionchanged:: 1.2.6 + Use Status enum class (was: string). +@@ -147,6 +151,11 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + for run_item in self.run_items: + run_item.reset() + ++ def _setup_context_for_run(self, context): ++ """Setup/Init runner context for run.""" ++ # -- OVERRIDDEN: By derived classes. ++ pass ++ + def __iter__(self): + return iter(self.run_items) + +@@ -204,7 +213,7 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + # feature_duration = self.run_endtime - self.run_starttime + return feature_duration + +- def walk_scenarios(self, with_outlines=False): ++ def walk_scenarios(self, with_outlines=False, with_rules=False): + """Provides a flat list of all scenarios of this ScenarioContainer. + A ScenarioOutline element adds its scenarios to this list. + But the ScenarioOutline element itself is only added when specified. +@@ -215,20 +224,20 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + :param with_outlines: If ScenarioOutline items should be added, too. + :return: List of all scenarios of this feature. + """ +- # TODO: Better use self.run_items + all_scenarios = [] +- # for scenario in self.scenarios: + for run_item in self.run_items: + if isinstance(run_item, Rule): + rule = run_item ++ if with_rules: ++ all_scenarios.append(rule) + all_scenarios.extend(rule.walk_scenarios(with_outlines=with_outlines)) +- if isinstance(run_item, ScenarioOutline): ++ elif isinstance(run_item, ScenarioOutline): + scenario_outline = run_item + if with_outlines: + all_scenarios.append(scenario_outline) + all_scenarios.extend(scenario_outline.scenarios) + else: +- assert isinstance(run_item, Scenario) ++ assert isinstance(run_item, Scenario), "OOPS: %r" % run_item + all_scenarios.append(run_item) + return all_scenarios + +@@ -274,9 +283,9 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + assert self.status == Status.skipped or self.hook_failed + + def skip(self, reason=None, require_not_executed=False): +- """Skip executing this feature or the remaining parts of it. +- Note that this feature may be already partly executed +- when this function is called. ++ """Skip executing this model entity or the remaining parts of it. ++ Note that this model entity (feature or rule) may be already partly ++ executed when this function is called. + + :param reason: Optional reason why feature should be skipped (as string). + :param require_not_executed: Optional, requires that feature is not +@@ -314,8 +323,8 @@ class ScenarioContainer(TagAndStatusStatement, Replayable): + hook_after_entity = "after_{0}".format(entity_name) + + runner.context._push(layer_name=entity_name) # pylint: disable=protected-access +- runner.context.feature = self + runner.context.tags = set(self.tags) ++ self._setup_context_for_run(runner.context) + + skip_entity_untested = runner.aborted + should_run_entity = self.should_run(runner.config) +@@ -497,6 +506,9 @@ class Feature(ScenarioContainer): + (self.name, len(self.run_items), + len(self.rules), len(self.scenarios)) + ++ def _setup_context_for_run(self, context): ++ context.feature = self ++ + def add_rule(self, rule): + """Add a rule to this feature.""" + feature = self +@@ -619,6 +631,9 @@ class Rule(ScenarioContainer): + self.parent = parent + self.feature = parent + ++ def _setup_context_for_run(self, context): ++ context.rule = self ++ + def __repr__(self): + return '' % \ + (self.name, len(self.scenarios)) +@@ -664,6 +679,10 @@ class Background(BasicStatement, Replayable): + + .. _`background`: gherkin.html#backgrounds + """ ++ # TODO: Background inheritance ++ # Rule.background should inherit its Feature.background steps (if available) ++ # Rule.background = Feature.background iff not Rule.background exists (ALREADY-SOLVED) ++ # Rule may override background inheritance mechanism + type = "background" + + def __init__(self, filename, line, keyword, name, steps=None, description=None): +@@ -796,7 +815,7 @@ class Scenario(TagAndStatusStatement, Replayable): + + @property + def background_steps(self): +- """Provide background steps if feature has a background. ++ """Provide background steps if feature/rule has a background. + Lazy init that copies the background steps. + + Note that a copy of the background steps is needed to ensure +diff --git a/behave/runner_util.py b/behave/runner_util.py +index 2210f78..7e0807f 100644 +--- a/behave/runner_util.py ++++ b/behave/runner_util.py +@@ -58,6 +58,100 @@ class FileLocationParser(object): + # ----------------------------------------------------------------------------- + # CLASSES: + # ----------------------------------------------------------------------------- ++from collections import OrderedDict ++from .model import Feature, Rule, ScenarioOutline, Scenario ++ ++ ++class FeatureLineDatabase(object): ++ """Helper class that supports select-by-location mechanism (FileLocation) ++ within a feature file by storing the feature line numbers for each entity. ++ ++ RESPONSIBILITY(s): ++ ++ * Can use the line number to select the best matching entity(s) in a feature ++ * Implements the select-by-location mechanism for each entity in the feature ++ """ ++ ++ def __init__(self, entity=None, line_data=None): ++ if entity and not line_data: ++ line_data = self.make_line_data_for(entity) ++ self.entity = entity ++ self.data = OrderedDict(line_data or []) ++ self._line_numbers = None ++ self._line_entities = None ++ ++ def select_run_item_by_line(self, line): ++ """Select one run-items by using the line number. ++ ++ * Exact match returns run-time entity (Feature, Rule, ScenarioOutline, Scenario) ++ * Any other line in between uses the predecessor entity ++ ++ :param line: Line number in Feature file (as int) ++ :return: Selected run-item object. ++ """ ++ run_item = self.data.get(line, None) ++ if run_item is None: ++ # -- CASE: BEST-MATCH in ordered line database ++ if self._line_numbers is None: ++ self._line_numbers = list(self.data.keys()) ++ self._line_entities = list(self.data.values()) ++ ++ pos = bisect(self._line_numbers, line) - 1 ++ if pos < 0: ++ pos = 0 ++ run_item = self._line_entities[pos] ++ return run_item ++ ++ def select_scenarios_by_line(self, line): ++ """Select one or more scenarios by using the line number. ++ ++ * line = 0: Selects all scenarios in the Feature file ++ * Feature / Rule / ScenarioOutline.location.line selects its scenarios ++ * Scenario.location.line selects the Scenario ++ * Any other lines use the predecessor entity (and its scenarios) ++ ++ :param line: Line number in Feature file (as int) ++ :return: List of selected scenarios ++ """ ++ run_item = self.select_run_item_by_line(line) ++ scenarios = [] ++ if isinstance(run_item, Feature): ++ scenarios = list(run_item.walk_scenarios()) ++ elif isinstance(run_item, Rule): ++ scenarios = list(run_item.walk_scenarios()) ++ elif isinstance(run_item, ScenarioOutline): ++ scenarios = list(run_item.scenarios) ++ elif isinstance(run_item, Scenario): ++ scenarios = [run_item] ++ return scenarios ++ ++ @classmethod ++ def make_line_data_for(cls, entity): ++ line_data = [] ++ run_items = [] ++ if isinstance(entity, Feature): ++ line_data.append((0, entity)) ++ run_items = entity.run_items ++ elif isinstance(entity, Rule): ++ run_items = entity.run_items ++ elif isinstance(entity, ScenarioOutline): ++ run_items = entity.scenarios ++ ++ line_data.append((entity.location.line, entity)) ++ for run_item in run_items: ++ line_data.extend(cls.make_line_data_for(run_item)) ++ # -- MAYBE: ++ # if isinstance(entity, ScenarioOutline) and run_items: ++ # # -- SPECIAL CASE: Lines after last Examples row => Use ScenarioOutline ++ # line_data.append((run_items[-1].location.line + 1, entity)) ++ return sorted(line_data) ++ ++ @classmethod ++ def make(cls, entity): ++ return cls(entity, cls.make_line_data_for(entity)) ++ ++ ++ + class FeatureScenarioLocationCollector(object): + """ + Collects FileLocation objects for a feature. +@@ -200,6 +294,94 @@ class FeatureScenarioLocationCollector(object): + return self.feature + + ++class FeatureScenarioLocationCollector1(FeatureScenarioLocationCollector): ++ ++ @staticmethod ++ def select_scenario_line_for(line, scenario_lines): ++ """ ++ Select scenario line for any given line. ++ ++ ALGORITHM: scenario.line <= line < next_scenario.line ++ ++ :param line: A line number in the file (as number). ++ :param scenario_lines: Sorted list of scenario lines. ++ :return: Scenario.line (first line) for the given line. ++ """ ++ if not scenario_lines: ++ return 0 # -- Select all scenarios. ++ pos = bisect(scenario_lines, line) - 1 ++ if pos < 0: ++ pos = 0 ++ return scenario_lines[pos] ++ ++ def discover_selected_scenarios(self, strict=False): ++ """ ++ Discovers selected scenarios based on the provided file locations. ++ In addition: ++ * discover all scenarios ++ * auto-correct BAD LINE-NUMBERS ++ ++ :param strict: If true, raises exception if file location is invalid. ++ :return: List of selected scenarios of this feature (as set). ++ :raises InvalidFileLocationError: ++ If file location is no exactly correct and strict is true. ++ """ ++ assert self.feature ++ if not self.all_scenarios: ++ self.all_scenarios = self.feature.walk_scenarios() ++ ++ # -- STEP: Check if lines are correct. ++ existing_lines = [scenario.line for scenario in self.all_scenarios] ++ selected_lines = list(self.scenario_lines) ++ for line in selected_lines: ++ new_line = self.select_scenario_line_for(line, existing_lines) ++ if new_line != line: ++ # -- AUTO-CORRECT BAD-LINE: ++ self.scenario_lines.remove(line) ++ self.scenario_lines.add(new_line) ++ if strict: ++ msg = "Scenario location '...:%d' should be: '%s:%d'" % \ ++ (line, self.filename, new_line) ++ raise InvalidFileLocationError(msg) ++ ++ # -- STEP: Determine selected scenarios and store them. ++ scenario_lines = set(self.scenario_lines) ++ selected_scenarios = set() ++ for scenario in self.all_scenarios: ++ if scenario.line in scenario_lines: ++ selected_scenarios.add(scenario) ++ scenario_lines.remove(scenario.line) ++ # -- CHECK ALL ARE RESOLVED: ++ assert not scenario_lines ++ return selected_scenarios ++ ++ ++class FeatureScenarioLocationCollector2(FeatureScenarioLocationCollector): ++ ++ def discover_selected_scenarios(self, strict=False): ++ """Discovers selected scenarios based on the provided file locations. ++ In addition: ++ * discover all scenarios ++ * auto-correct BAD LINE-NUMBERS ++ ++ :param strict: If true, raises exception if file location is invalid. ++ :return: List of selected scenarios of this feature (as set). ++ :raises InvalidFileLocationError: ++ If file location is no exactly correct and strict is true. ++ """ ++ assert self.feature ++ if not self.all_scenarios: ++ self.all_scenarios = self.feature.walk_scenarios() ++ ++ line_database = FeatureLineDatabase.make(self.feature) ++ selected_lines = list(self.scenario_lines) ++ selected_scenarios = set() ++ for line in selected_lines: ++ more_scenarios = line_database.select_scenarios_by_line(line) ++ selected_scenarios.update(more_scenarios) ++ return selected_scenarios ++ ++ + class FeatureListParser(object): + """ + Read textual file, ala '@features.txt'. This file contains: +@@ -304,7 +486,7 @@ def parse_features(feature_files, language=None): + :param language: Default language to use. + :return: List of feature objects. + """ +- scenario_collector = FeatureScenarioLocationCollector() ++ scenario_collector = FeatureScenarioLocationCollector2() + features = [] + for location in feature_files: + if not isinstance(location, FileLocation): +@@ -315,7 +497,7 @@ def parse_features(feature_files, language=None): + scenario_collector.add_location(location) + continue + elif scenario_collector.feature: +- # -- ADD CURRENT FEATURE: As collection of scenarios. ++ # -- NEW FEATURE DETECTED: Add current feature. + current_feature = scenario_collector.build_feature() + features.append(current_feature) + scenario_collector.clear() +diff --git a/features/runner.select_scenarios_by_file_location.feature b/features/runner.select_scenarios_by_file_location.feature +index f60c43f..69e23fe 100644 +--- a/features/runner.select_scenarios_by_file_location.feature ++++ b/features/runner.select_scenarios_by_file_location.feature +@@ -13,15 +13,28 @@ Feature: Select Scenarios by File Location + . * A file location with filename but without line number + . refers to the complete file + . * A file location with line number 0 (zero) refers to the complete file ++ . * A file location within a scenario container (Feature, Rule, ScenarioOutline), ++ . that does not refer to the file location within a scenario, ++ . selects all scenarios of this scenario container. + . + . SPECIFICATION: Scenario selection by file locations + . * scenario.line == file_location.line selects scenario (preferred method). + . * Any line number in the following range is acceptable: +- . scenario.line <= file_location.line < next_scenario.line +- . * The first scenario is selected, +- . if the file location line number is less than first scenario.line. ++ . scenario.line <= file_location.line < next_entity.line (maybe: scenario) ++ . * If the file location line number is less than first scenario.line, ++ . the preceeding scenario container (Feature or Rule) is selected. + . * The last scenario is selected, + . if the file location line number is greater than the lines in the file. ++ . * For ScenarioOutline.scenarios: ++ . scenario.line == ScenarioOutline.examples[x].row.line ++ . The line number of the Examples row that created the scenario is assigned to it. ++ . ++ . SPECIFICATION: "Scenario container" selection by file locations ++ . * Scenario containers are: Feature, Rule, ScenarioOutline ++ . * A file location that points into the matching range of a scenario container, ++ . selects all scenarios / run-items within this scenario container. ++ . * Any line number in the following range selects the scenario container: ++ . entity.line <= file_location.line < next_entity.line (maybe: child) + . + . SPECIFICATION: Runner with scenario locations (file locations) + . * Adjacent file locations are merged if they refer to the same file, like: +@@ -162,22 +175,24 @@ Feature: Select Scenarios by File Location + """ + + @file_location.select_first +- Scenario: Select first scenario if line number is smaller than first scenario line ++ Scenario: Select all scenarios if line number is smaller than first scenario line + + CASE: 0 < file_location.line < first_scenario.line ++ HINT: Any line number outside of a scenario may point into a "scenario container". ++ In this case, all the scenarios of the scenario container are selected. + + When I run "behave -f plain --dry-run --no-skipped features/alice.feature:1" + Then it should pass with: + """ + 0 features passed, 0 failed, 0 skipped, 1 untested +- 0 scenarios passed, 0 failed, 1 skipped, 1 untested ++ 0 scenarios passed, 0 failed, 0 skipped, 2 untested + """ + And the command output should contain: + """ + Feature: Alice + Scenario: Alice First + """ +- But the command output should not contain: ++ But the command output should contain: + """ + Scenario: Alice Last + """ +diff --git a/pytest.ini b/pytest.ini +index a686596..ff2a8a2 100644 +--- a/pytest.ini ++++ b/pytest.ini +@@ -16,7 +16,7 @@ + # ============================================================================ + + [pytest] +-minversion = 4.2 ++minversion = 2.8 + testpaths = tests + python_files = test_*.py + addopts = --metadata PACKAGE_UNDER_TEST behave +diff --git a/tests/unit/test_runner_util.py b/tests/unit/test_runner_util.py +new file mode 100644 +index 0000000..b5019b8 +--- /dev/null ++++ b/tests/unit/test_runner_util.py +@@ -0,0 +1,175 @@ ++# -*- coding: UTF-8 -*- ++ ++from __future__ import absolute_import, print_function ++from collections import OrderedDict ++from behave.runner_util import FeatureLineDatabase ++from behave.parser import parse_feature ++from behave.model import Feature, Rule, ScenarioOutline, Scenario, Background ++import pytest ++ ++ ++# --------------------------------------------------------------------------------------- ++# TEST DATA: FeatureLineDatabase ++# --------------------------------------------------------------------------------------- ++feature_text1 = u""" ++ Feature: Alice ++ Background: Alice.Background ++ Given a background step passes ++ ++ Scenario: A1 ++ Given a scenario step passes ++ ++ Scenario: A2 ++ Given a scenario step passes ++ When a scenario step passes ++ """ ++ ++feature_text_with_scenario_outline = u""" ++ Feature: Bob ++ ++ Scenario Outline: Bob.SO_2_ ++ Given a person with name "" ++ Then the person is born in ++ ++ Examples: ++ | Name | Birthyear | ++ | Alice | 1990 | ++ | Bob | 1991 | ++ ++ Scenario: Bob.S3 ++ Given a scenario step passes ++ When a scenario step passes ++ """ ++ ++feature_text_with_rule = u""" ++ Feature: Charly ++ Background: Charly.Background ++ Given a background step passes ++ ++ Scenario: C1 ++ Given a scenario step passes ++ ++ Rule: Charly.Rule_1 ++ ++ Scenario: Rule_1.C2 ++ Given a scenario step passes ++ When a scenario step passes ++ """ ++ ++feature_file_map = { ++ "basic.feature": feature_text1, ++ "scenario_outline.feature": feature_text_with_scenario_outline, ++ "rule.feature": feature_text_with_rule, ++} ++ ++# --------------------------------------------------------------------------------------- ++# TEST SUITE FOR: FeatureLineDatabase ++# --------------------------------------------------------------------------------------- ++class TestFeatureLineDatabase(object): ++ def test_make(self): ++ feature = parse_feature(feature_text1.strip(), ++ filename="features/Alice.feature") ++ scenario_0 = feature.scenarios[0] ++ scenario_1 = feature.scenarios[1] ++ ++ line_database = FeatureLineDatabase.make(feature) ++ expected = OrderedDict([ ++ (0, feature), ++ (feature.location.line, feature), ++ (scenario_0.line, scenario_0), ++ (scenario_1.line, scenario_1), ++ ]) ++ assert line_database.data == expected ++ assert feature.location.line == 1 ++ ++ def test_make__with_scenario_outline(self): ++ feature = parse_feature(feature_text_with_scenario_outline.strip(), ++ filename="features/Bob.feature") ++ scenarios = feature.walk_scenarios(with_outlines=True) ++ scenario_outline = scenarios[0] ++ assert scenario_outline is feature.run_items[0] ++ scenario_1 = scenarios[1] ++ scenario_2 = scenarios[2] ++ scenario_3 = scenarios[3] ++ ++ line_database = FeatureLineDatabase.make(feature) ++ expected = OrderedDict([ ++ (0, feature), ++ (feature.location.line, feature), ++ (scenario_outline.line, scenario_outline), ++ (scenario_1.line, scenario_1), ++ (scenario_2.line, scenario_2), ++ (scenario_3.line, scenario_3), ++ ]) ++ assert line_database.data == expected ++ assert feature.location.line < scenario_outline.location.line ++ assert scenario_outline.location.line < scenario_1.location.line ++ assert scenario_1.location.line < scenario_2.location.line ++ assert scenario_2.location.line < scenario_3.location.line ++ ++ ++ def test_select_run_items_by_line__feature_line_selects_feature(self): ++ feature = parse_feature(feature_text1, filename="features/Alice.feature") ++ line_database = FeatureLineDatabase.make(feature) ++ selected = line_database.select_run_item_by_line(feature.location.line) ++ assert selected is feature ++ assert isinstance(selected, Feature) ++ ++ @pytest.mark.parametrize("filename", [ ++ "basic.feature", "scenario_outline.feature", "rule.feature" ++ ]) ++ def test_select_run_items_by_line__entity_line_selects_entity(self, filename): ++ feature_text = feature_file_map[filename] ++ feature = parse_feature(feature_text, filename=filename) ++ line_database = FeatureLineDatabase.make(feature) ++ last_line = 0 ++ all_run_items = feature.walk_scenarios(with_outlines=True, with_rules=True) ++ for run_item in all_run_items: ++ selected = line_database.select_run_item_by_line(run_item.location.line) ++ assert selected is run_item ++ assert last_line < selected.location.line ++ last_line = run_item.location.line ++ ++ @pytest.mark.parametrize("filename", [ ++ "basic.feature", "scenario_outline.feature", "rule.feature" ++ ]) ++ def test_select_run_items_by_line__line_before_entity_selects_last_entity(self, filename): ++ feature_text = feature_file_map[filename] ++ feature = parse_feature(feature_text, filename=filename) ++ line_database = FeatureLineDatabase.make(feature) ++ all_run_items = feature.walk_scenarios(with_outlines=True, with_rules=True) ++ last_run_item = feature ++ for run_item in all_run_items: ++ predecessor_line = run_item.location.line - 1 ++ selected = line_database.select_run_item_by_line(predecessor_line) ++ assert selected is last_run_item ++ assert selected is not run_item ++ last_run_item = run_item ++ ++ @pytest.mark.parametrize("filename", [ ++ "basic.feature", "scenario_outline.feature", "rule.feature" ++ ]) ++ def test_select_run_items_by_line__line_after_entity_selects_entity(self, filename): ++ # -- HINT: In most cases ++ # EXCEPT: ++ # * Scenarios of ScenarioOutline: scenario.line == SO.examples.row.line ++ # * Empty entity without steps is directly followed by other entity ++ feature_text = feature_file_map[filename] ++ feature = parse_feature(feature_text, filename=filename) ++ line_database = FeatureLineDatabase.make(feature) ++ all_run_items = feature.walk_scenarios(with_outlines=True, with_rules=True) ++ file_end_line = all_run_items[-1].location.line + 1000 ++ for index, run_item in enumerate(all_run_items): ++ next_line = run_item.location.line + 1 ++ next_entity_line = file_end_line ++ if index+1 < len(all_run_items): ++ next_entity = all_run_items[index+1] ++ next_entity_line = next_entity.line ++ if next_line >= next_entity_line: ++ # -- EXCLUDE: Scenarios in a ScenarioOutline ++ print("EXCLUDED: %s: %s (line=%s)" % ++ (run_item.keyword, run_item.name, run_item.line)) ++ continue ++ ++ selected = line_database.select_run_item_by_line(next_line) ++ assert selected is run_item diff --git a/meta-python/recipes-devtools/python/python3-behave/0038-docs-Add-description-for-Select-by-location-for-Scen.patch b/meta-python/recipes-devtools/python/python3-behave/0038-docs-Add-description-for-Select-by-location-for-Scen.patch new file mode 100644 index 000000000..14394603f --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0038-docs-Add-description-for-Select-by-location-for-Scen.patch @@ -0,0 +1,58 @@ +From a6388c0e97e4378a4ef628361a46ff6d02d3159e Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 10:50:54 +0200 +Subject: [PATCH] docs: Add description for "Select-by-location for Scenario + Containers" + +--- + docs/new_and_noteworthy_v1.2.7.rst | 33 ++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/docs/new_and_noteworthy_v1.2.7.rst b/docs/new_and_noteworthy_v1.2.7.rst +index 80d9576..ff1cd1f 100644 +--- a/docs/new_and_noteworthy_v1.2.7.rst ++++ b/docs/new_and_noteworthy_v1.2.7.rst +@@ -7,6 +7,7 @@ Summary: + * Use/Support :pypi:`cucumber-tag-expressions` (superceed: old-style tag-expressions) + * :pypi:`cucumber-tag-expressions` are extended by "tag-matching" + to match partial tag names, like: ``@foo.*`` ++* `Select-by-location for Scenario Containers`_ (Feature, Rule, ScenarioOutline) + + .. _`Example Mapping`: https://cucumber.io/blog/example-mapping-introduction/ + .. _`Example Mapping Webinar`: https://cucumber.io/blog/example-mapping-webinar/ +@@ -102,3 +103,35 @@ Overview of the `Example Mapping`_ concepts: + + + .. include:: _content.tag_expressions_v2.rst ++ ++ ++Select-by-location for Scenario Containers ++------------------------------------------------------------------------------- ++ ++In the past, it was already possible to scenario(s) by using its **file-location**. ++ ++A **file-location** has the schema: ``:``. ++Example: ``features/alice.feature:12`` ++(refers to ``line 12`` in ``features/alice.feature`` file). ++ ++Rules to select **Scenarios** by using the file-location: ++ ++* **Scenario:** Use a file-location that points to the keyword/title or its steps ++ (until next Scenario/Entity starts). ++ ++* **Scenario of a ScenarioOutline:** ++ Use the file-location of its Examples row. ++ ++Now you can select all entities of a **Scenario Container** (Feature, Rule, ScenarioOutline): ++ ++* **Feature:** ++ Use file-location before first contained entity/Scenario starts. ++ ++* **Rule:** ++ Use file-location from keyword/title line to line before its first Scenario/Background. ++ ++* **ScenarioOutline:** ++ Use file-location from keyword/title line to line before its Examples rows. ++ ++A file-location into a **Scenario Container** selects all its entities ++(Scenarios, ...). diff --git a/meta-python/recipes-devtools/python/python3-behave/0039-tests-Fix-warnings-for-python3.patch b/meta-python/recipes-devtools/python/python3-behave/0039-tests-Fix-warnings-for-python3.patch new file mode 100644 index 000000000..2b80d5df1 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0039-tests-Fix-warnings-for-python3.patch @@ -0,0 +1,50 @@ +From 93f578698844d7276e5b2f287ee7ff3bcfc049bf Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 13:35:51 +0200 +Subject: [PATCH] tests: Fix warnings for python3. + +--- + tests/issues/test_issue0336.py | 1 + + tests/unit/test_parser.py | 11 +++++++---- + 2 files changed, 8 insertions(+), 4 deletions(-) + +diff --git a/tests/issues/test_issue0336.py b/tests/issues/test_issue0336.py +index eb4c3fd..09201ae 100644 +--- a/tests/issues/test_issue0336.py ++++ b/tests/issues/test_issue0336.py +@@ -52,6 +52,7 @@ AssertionError + assert file_line_text in text2 + + # @require_python2 ++ @pytest.mark.filterwarnings("ignore:invalid escape sequence") + def test__problem_exists_with_problematic_encoding(self): + """Test ensures that problem exists with encoding=unicode-escape""" + # -- NOTE: Explicit use of problematic encoding +diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py +index ecbb1bf..01006f9 100644 +--- a/tests/unit/test_parser.py ++++ b/tests/unit/test_parser.py +@@ -564,16 +564,19 @@ Feature: Stuff + ('then', 'Then', 'stuff is in buckets', None, None), + ]) + ++ @pytest.mark.filterwarnings("ignore:invalid escape sequence") + def test_parses_feature_with_table_and_escaped_pipe_in_cell_values(self): ++ # -- HINT py37: DeprecationWarning: invalid escape sequence '\|' ++ # USE: Double escaped-backslashes. + doc = u''' + Feature: + Scenario: + Given we have special cell values: + | name | value | +- | alice | one\|two | +- | bob |\|one | +- | charly | one\|| +- | doro | one\|two\|three\|four | ++ | alice | one\\|two | ++ | bob |\\|one | ++ | charly | one\\|| ++ | doro | one\\|two\\|three\\|four | + '''.lstrip() + feature = parser.parse_feature(doc) + assert len(feature.scenarios) == 1 diff --git a/meta-python/recipes-devtools/python/python3-behave/0040-Use-cucumber-tag-expressions-1.1.2-to-fix-warnings.patch b/meta-python/recipes-devtools/python/python3-behave/0040-Use-cucumber-tag-expressions-1.1.2-to-fix-warnings.patch new file mode 100644 index 000000000..85c1bc16a --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0040-Use-cucumber-tag-expressions-1.1.2-to-fix-warnings.patch @@ -0,0 +1,55 @@ +From 21a399bf6e2e2eb09c98d173f1d0975bcecbf1e5 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 13:36:59 +0200 +Subject: [PATCH] Use cucumber-tag-expressions 1.1.2 (to fix warnings). + +py.requirements: Add twine, bump2version +--- + py.requirements/basic.txt | 2 +- + py.requirements/develop.txt | 6 +++++- + setup.py | 2 +- + 3 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/py.requirements/basic.txt b/py.requirements/basic.txt +index 3b71bfb..ad5b9a6 100644 +--- a/py.requirements/basic.txt ++++ b/py.requirements/basic.txt +@@ -8,7 +8,7 @@ + # * http://www.pip-installer.org/ + # ============================================================================ + +-cucumber-tag-expressions >= 1.1.1 ++cucumber-tag-expressions >= 1.1.2 + parse >= 1.8.2 + parse_type >= 0.4.2 + six >= 1.12.0 +diff --git a/py.requirements/develop.txt b/py.requirements/develop.txt +index d823389..a16d7bf 100644 +--- a/py.requirements/develop.txt ++++ b/py.requirements/develop.txt +@@ -11,7 +11,11 @@ pathlib; python_version <= '3.4' + pycmd + + # -- CONFIGURATION MANAGEMENT (helpers): +-bumpversion >= 0.4.0 ++# FORMER: bumpversion >= 0.4.0 ++bump2version >= 0.5.6 ++ ++# -- RELEASE MANAGEMENT: Push package to pypi. ++twine >= 1.13.0 + + # -- DEVELOPMENT SUPPORT: + tox >= 1.8.1 +diff --git a/setup.py b/setup.py +index 9c7560d..cea4392 100644 +--- a/setup.py ++++ b/setup.py +@@ -76,7 +76,7 @@ setup( + # SUPPORT: python2.7, python3.3 (or higher) + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*", + install_requires=[ +- "cucumber-tag-expressions >= 1.1.1", ++ "cucumber-tag-expressions >= 1.1.2", + "parse >= 1.8.2", + "parse_type >= 0.4.2", + "six >= 1.12.0", diff --git a/meta-python/recipes-devtools/python/python3-behave/0041-MENTION-ENHANCEMENT-Support-emojis-in-feature-files-.patch b/meta-python/recipes-devtools/python/python3-behave/0041-MENTION-ENHANCEMENT-Support-emojis-in-feature-files-.patch new file mode 100644 index 000000000..6d69f4754 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-behave/0041-MENTION-ENHANCEMENT-Support-emojis-in-feature-files-.patch @@ -0,0 +1,91 @@ +From 1d3c288a2eed8caef24356e2a92483425bee0378 Mon Sep 17 00:00:00 2001 +From: jenisys +Date: Sat, 6 Jul 2019 14:18:16 +0200 +Subject: [PATCH] MENTION ENHANCEMENT: Support emojis in feature files and + steps. + +--- + CHANGES.rst | 3 ++- + docs/new_and_noteworthy_v1.2.7.rst | 17 +++++++++++++++++ + features/i18n_emoji.feature | 7 +++++++ + features/steps/i18n_emoji_steps.py | 10 ++++++++++ + 4 files changed, 36 insertions(+), 1 deletion(-) + create mode 100644 features/i18n_emoji.feature + create mode 100644 features/steps/i18n_emoji_steps.py + +diff --git a/CHANGES.rst b/CHANGES.rst +index 01bd1bd..d165275 100644 +--- a/CHANGES.rst ++++ b/CHANGES.rst +@@ -24,8 +24,9 @@ GOALS: + ENHANCEMENTS: + + * Add support for Gherkin v6 grammar and syntax in ``*.feature`` files +-* Use cucumber "gherkin-languages.json" now (simplify: Gherkin v6 aliases, language usage) + * Use `cucumber-tag-expressions`_ with tag-matching extension (superceeds: old-style tag-expressions) ++* Use cucumber "gherkin-languages.json" now (simplify: Gherkin v6 aliases, language usage) ++* Support emojis in ``*.feature`` files and steps + * issue #678: Scenario Outline: Support tags with commas and semicolons (provided by: lawnmowerlatte, pull #679) + * issue #675: Feature files cannot be found within symlink directories (provided by: smadness, pull #680) + * Select-by-location: Add support for "Scenario container" (Feature, Rule, ScenarioOutline) (related to: #391) +diff --git a/docs/new_and_noteworthy_v1.2.7.rst b/docs/new_and_noteworthy_v1.2.7.rst +index ff1cd1f..b7242f7 100644 +--- a/docs/new_and_noteworthy_v1.2.7.rst ++++ b/docs/new_and_noteworthy_v1.2.7.rst +@@ -8,6 +8,7 @@ Summary: + * :pypi:`cucumber-tag-expressions` are extended by "tag-matching" + to match partial tag names, like: ``@foo.*`` + * `Select-by-location for Scenario Containers`_ (Feature, Rule, ScenarioOutline) ++* `Support for emojis in feature files and steps`_ + + .. _`Example Mapping`: https://cucumber.io/blog/example-mapping-introduction/ + .. _`Example Mapping Webinar`: https://cucumber.io/blog/example-mapping-webinar/ +@@ -135,3 +136,19 @@ Now you can select all entities of a **Scenario Container** (Feature, Rule, Scen + + A file-location into a **Scenario Container** selects all its entities + (Scenarios, ...). ++ ++ ++Support for Emojis in Feature Files and Steps ++------------------------------------------------------------------------------- ++ ++* Emojis can now be used in ``*.feature`` files. ++* Emojis can now be used in step definitions. ++* You can now use ``language=emoji (em)`` in ``*.feature`` files ;-) ++ ++.. literalinclude:: ../features/i18n_emoji.feature ++ :prepend: # -- FILE: features/i18n_emoji.feature ++ :language: gherkin ++ ++.. literalinclude:: ../features/steps/i18n_emoji_steps.py ++ :prepend: # -- FILE: features/steps/i18n_emoji_steps.py ++ :language: python +diff --git a/features/i18n_emoji.feature b/features/i18n_emoji.feature +new file mode 100644 +index 0000000..db23ac2 +--- /dev/null ++++ b/features/i18n_emoji.feature +@@ -0,0 +1,7 @@ ++# language: em ++# SOURCE: https://github.com/cucumber/cucumber/blob/master/gherkin/testdata/good/i18n_emoji.feature ++ ++