busser-behave 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.cane +0 -0
- data/.gitignore +17 -0
- data/.tailor +4 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE +15 -0
- data/README.md +41 -0
- data/Rakefile +68 -0
- data/busser-behave.gemspec +30 -0
- data/features/plugin_install_command.feature +11 -0
- data/features/plugin_list_command.feature +8 -0
- data/features/support/env.rb +13 -0
- data/features/test_command.feature +31 -0
- data/lib/busser/behave/version.rb +26 -0
- data/lib/busser/runner_plugin/behave.rb +37 -0
- data/vendor/behave/CHANGES.rst +483 -0
- data/vendor/behave/LICENSE +23 -0
- data/vendor/behave/MANIFEST.in +37 -0
- data/vendor/behave/PROJECT_INFO.rst +21 -0
- data/vendor/behave/README.rst +112 -0
- data/vendor/behave/VERSION.txt +1 -0
- data/vendor/behave/behave.ini +22 -0
- data/vendor/behave/behave/__init__.py +30 -0
- data/vendor/behave/behave/__main__.py +187 -0
- data/vendor/behave/behave/_stepimport.py +185 -0
- data/vendor/behave/behave/_types.py +134 -0
- data/vendor/behave/behave/api/__init__.py +7 -0
- data/vendor/behave/behave/api/async_step.py +283 -0
- data/vendor/behave/behave/capture.py +227 -0
- data/vendor/behave/behave/compat/__init__.py +5 -0
- data/vendor/behave/behave/compat/collections.py +20 -0
- data/vendor/behave/behave/configuration.py +788 -0
- data/vendor/behave/behave/contrib/__init__.py +0 -0
- data/vendor/behave/behave/contrib/scenario_autoretry.py +73 -0
- data/vendor/behave/behave/formatter/__init__.py +12 -0
- data/vendor/behave/behave/formatter/_builtins.py +39 -0
- data/vendor/behave/behave/formatter/_registry.py +135 -0
- data/vendor/behave/behave/formatter/ansi_escapes.py +91 -0
- data/vendor/behave/behave/formatter/base.py +200 -0
- data/vendor/behave/behave/formatter/formatters.py +57 -0
- data/vendor/behave/behave/formatter/json.py +253 -0
- data/vendor/behave/behave/formatter/null.py +12 -0
- data/vendor/behave/behave/formatter/plain.py +158 -0
- data/vendor/behave/behave/formatter/pretty.py +351 -0
- data/vendor/behave/behave/formatter/progress.py +287 -0
- data/vendor/behave/behave/formatter/rerun.py +114 -0
- data/vendor/behave/behave/formatter/sphinx_steps.py +372 -0
- data/vendor/behave/behave/formatter/sphinx_util.py +118 -0
- data/vendor/behave/behave/formatter/steps.py +497 -0
- data/vendor/behave/behave/formatter/tags.py +178 -0
- data/vendor/behave/behave/i18n.py +614 -0
- data/vendor/behave/behave/importer.py +102 -0
- data/vendor/behave/behave/json_parser.py +264 -0
- data/vendor/behave/behave/log_capture.py +233 -0
- data/vendor/behave/behave/matchers.py +402 -0
- data/vendor/behave/behave/model.py +1737 -0
- data/vendor/behave/behave/model_core.py +416 -0
- data/vendor/behave/behave/model_describe.py +105 -0
- data/vendor/behave/behave/parser.py +615 -0
- data/vendor/behave/behave/reporter/__init__.py +0 -0
- data/vendor/behave/behave/reporter/base.py +45 -0
- data/vendor/behave/behave/reporter/junit.py +473 -0
- data/vendor/behave/behave/reporter/summary.py +94 -0
- data/vendor/behave/behave/runner.py +753 -0
- data/vendor/behave/behave/runner_util.py +417 -0
- data/vendor/behave/behave/step_registry.py +112 -0
- data/vendor/behave/behave/tag_expression.py +111 -0
- data/vendor/behave/behave/tag_matcher.py +465 -0
- data/vendor/behave/behave/textutil.py +137 -0
- data/vendor/behave/behave/userdata.py +130 -0
- data/vendor/behave/behave4cmd0/__all_steps__.py +12 -0
- data/vendor/behave/behave4cmd0/__init__.py +5 -0
- data/vendor/behave/behave4cmd0/__setup.py +11 -0
- data/vendor/behave/behave4cmd0/command_shell.py +216 -0
- data/vendor/behave/behave4cmd0/command_shell_proc.py +256 -0
- data/vendor/behave/behave4cmd0/command_steps.py +532 -0
- data/vendor/behave/behave4cmd0/command_util.py +147 -0
- data/vendor/behave/behave4cmd0/failing_steps.py +49 -0
- data/vendor/behave/behave4cmd0/log/__init__.py +1 -0
- data/vendor/behave/behave4cmd0/log/steps.py +395 -0
- data/vendor/behave/behave4cmd0/note_steps.py +29 -0
- data/vendor/behave/behave4cmd0/passing_steps.py +36 -0
- data/vendor/behave/behave4cmd0/pathutil.py +146 -0
- data/vendor/behave/behave4cmd0/setup_command_shell.py +24 -0
- data/vendor/behave/behave4cmd0/textutil.py +304 -0
- data/vendor/behave/bin/behave +44 -0
- data/vendor/behave/bin/behave.cmd +10 -0
- data/vendor/behave/bin/behave.junit_filter.py +85 -0
- data/vendor/behave/bin/behave.step_durations.py +163 -0
- data/vendor/behave/bin/behave2cucumber_json.py +63 -0
- data/vendor/behave/bin/behave_cmd.py +44 -0
- data/vendor/behave/bin/convert_i18n_yaml.py +77 -0
- data/vendor/behave/bin/explore_platform_encoding.py +24 -0
- data/vendor/behave/bin/i18n.yml +621 -0
- data/vendor/behave/bin/invoke +8 -0
- data/vendor/behave/bin/invoke.cmd +9 -0
- data/vendor/behave/bin/json.format.py +167 -0
- data/vendor/behave/bin/jsonschema_validate.py +122 -0
- data/vendor/behave/bin/make_localpi.py +279 -0
- data/vendor/behave/bin/project_bootstrap.sh +30 -0
- data/vendor/behave/bin/toxcmd.py +270 -0
- data/vendor/behave/bin/toxcmd3.py +270 -0
- data/vendor/behave/conftest.py +27 -0
- data/vendor/behave/docs/Makefile +154 -0
- data/vendor/behave/docs/_static/agogo.css +501 -0
- data/vendor/behave/docs/_static/behave_logo.png +0 -0
- data/vendor/behave/docs/_static/behave_logo1.png +0 -0
- data/vendor/behave/docs/_static/behave_logo2.png +0 -0
- data/vendor/behave/docs/_static/behave_logo3.png +0 -0
- data/vendor/behave/docs/_themes/LICENSE +45 -0
- data/vendor/behave/docs/_themes/kr/layout.html +17 -0
- data/vendor/behave/docs/_themes/kr/relations.html +19 -0
- data/vendor/behave/docs/_themes/kr/static/flasky.css_t +480 -0
- data/vendor/behave/docs/_themes/kr/static/small_flask.css +90 -0
- data/vendor/behave/docs/_themes/kr/theme.conf +7 -0
- data/vendor/behave/docs/_themes/kr_small/layout.html +22 -0
- data/vendor/behave/docs/_themes/kr_small/static/flasky.css_t +287 -0
- data/vendor/behave/docs/_themes/kr_small/theme.conf +10 -0
- data/vendor/behave/docs/api.rst +408 -0
- data/vendor/behave/docs/appendix.rst +19 -0
- data/vendor/behave/docs/behave.rst +640 -0
- data/vendor/behave/docs/behave.rst-template +86 -0
- data/vendor/behave/docs/behave_ecosystem.rst +81 -0
- data/vendor/behave/docs/comparison.rst +85 -0
- data/vendor/behave/docs/conf.py +293 -0
- data/vendor/behave/docs/context_attributes.rst +66 -0
- data/vendor/behave/docs/django.rst +192 -0
- data/vendor/behave/docs/formatters.rst +61 -0
- data/vendor/behave/docs/gherkin.rst +673 -0
- data/vendor/behave/docs/index.rst +57 -0
- data/vendor/behave/docs/install.rst +60 -0
- data/vendor/behave/docs/more_info.rst +184 -0
- data/vendor/behave/docs/new_and_noteworthy.rst +18 -0
- data/vendor/behave/docs/new_and_noteworthy_v1.2.4.rst +11 -0
- data/vendor/behave/docs/new_and_noteworthy_v1.2.5.rst +814 -0
- data/vendor/behave/docs/new_and_noteworthy_v1.2.6.rst +255 -0
- data/vendor/behave/docs/parse_builtin_types.rst +59 -0
- data/vendor/behave/docs/philosophy.rst +235 -0
- data/vendor/behave/docs/regular_expressions.rst +71 -0
- data/vendor/behave/docs/related.rst +14 -0
- data/vendor/behave/docs/test_domains.rst +62 -0
- data/vendor/behave/docs/tutorial.rst +636 -0
- data/vendor/behave/docs/update_behave_rst.py +100 -0
- data/vendor/behave/etc/json/behave.json-schema +172 -0
- data/vendor/behave/etc/junit.xml/behave_junit.xsd +103 -0
- data/vendor/behave/etc/junit.xml/junit-4.xsd +92 -0
- data/vendor/behave/examples/async_step/README.txt +8 -0
- data/vendor/behave/examples/async_step/behave.ini +14 -0
- data/vendor/behave/examples/async_step/features/async_dispatch.feature +8 -0
- data/vendor/behave/examples/async_step/features/async_run.feature +6 -0
- data/vendor/behave/examples/async_step/features/environment.py +28 -0
- data/vendor/behave/examples/async_step/features/steps/async_dispatch_steps.py +26 -0
- data/vendor/behave/examples/async_step/features/steps/async_steps34.py +10 -0
- data/vendor/behave/examples/async_step/features/steps/async_steps35.py +10 -0
- data/vendor/behave/examples/async_step/testrun_example.async_dispatch.txt +11 -0
- data/vendor/behave/examples/async_step/testrun_example.async_run.txt +9 -0
- data/vendor/behave/examples/env_vars/README.rst +26 -0
- data/vendor/behave/examples/env_vars/behave.ini +15 -0
- data/vendor/behave/examples/env_vars/behave_run.output_example.txt +12 -0
- data/vendor/behave/examples/env_vars/features/env_var.feature +6 -0
- data/vendor/behave/examples/env_vars/features/steps/env_var_steps.py +38 -0
- data/vendor/behave/features/README.txt +12 -0
- data/vendor/behave/features/background.feature +392 -0
- data/vendor/behave/features/capture_stderr.feature +172 -0
- data/vendor/behave/features/capture_stdout.feature +125 -0
- data/vendor/behave/features/cmdline.lang_list.feature +33 -0
- data/vendor/behave/features/configuration.default_paths.feature +116 -0
- data/vendor/behave/features/context.global_params.feature +35 -0
- data/vendor/behave/features/context.local_params.feature +17 -0
- data/vendor/behave/features/directory_layout.advanced.feature +147 -0
- data/vendor/behave/features/directory_layout.basic.feature +75 -0
- data/vendor/behave/features/directory_layout.basic2.feature +87 -0
- data/vendor/behave/features/environment.py +53 -0
- data/vendor/behave/features/exploratory_testing.with_table.feature +141 -0
- data/vendor/behave/features/feature.description.feature +0 -0
- data/vendor/behave/features/feature.exclude_from_run.feature +96 -0
- data/vendor/behave/features/formatter.help.feature +30 -0
- data/vendor/behave/features/formatter.json.feature +420 -0
- data/vendor/behave/features/formatter.progress3.feature +235 -0
- data/vendor/behave/features/formatter.rerun.feature +296 -0
- data/vendor/behave/features/formatter.steps.feature +181 -0
- data/vendor/behave/features/formatter.steps_catalog.feature +100 -0
- data/vendor/behave/features/formatter.steps_doc.feature +140 -0
- data/vendor/behave/features/formatter.steps_usage.feature +404 -0
- data/vendor/behave/features/formatter.tags.feature +134 -0
- data/vendor/behave/features/formatter.tags_location.feature +183 -0
- data/vendor/behave/features/formatter.user_defined.feature +196 -0
- data/vendor/behave/features/i18n.unicode_problems.feature +445 -0
- data/vendor/behave/features/logcapture.clear_handlers.feature +114 -0
- data/vendor/behave/features/logcapture.feature +188 -0
- data/vendor/behave/features/logcapture.filter.feature +130 -0
- data/vendor/behave/features/logging.no_capture.feature +99 -0
- data/vendor/behave/features/logging.setup_format.feature +157 -0
- data/vendor/behave/features/logging.setup_level.feature +168 -0
- data/vendor/behave/features/logging.setup_with_configfile.feature +137 -0
- data/vendor/behave/features/parser.background.sad_cases.feature +129 -0
- data/vendor/behave/features/parser.feature.sad_cases.feature +144 -0
- data/vendor/behave/features/runner.abort_by_user.feature +305 -0
- data/vendor/behave/features/runner.continue_after_failed_step.feature +136 -0
- data/vendor/behave/features/runner.default_format.feature +175 -0
- data/vendor/behave/features/runner.dry_run.feature +184 -0
- data/vendor/behave/features/runner.feature_listfile.feature +223 -0
- data/vendor/behave/features/runner.hook_errors.feature +382 -0
- data/vendor/behave/features/runner.multiple_formatters.feature +285 -0
- data/vendor/behave/features/runner.scenario_autoretry.feature +131 -0
- data/vendor/behave/features/runner.select_files_by_regexp.example.feature +71 -0
- data/vendor/behave/features/runner.select_files_by_regexp.feature +84 -0
- data/vendor/behave/features/runner.select_scenarios_by_file_location.feature +403 -0
- data/vendor/behave/features/runner.select_scenarios_by_name.feature +289 -0
- data/vendor/behave/features/runner.select_scenarios_by_tag.feature +225 -0
- data/vendor/behave/features/runner.stop_after_failure.feature +122 -0
- data/vendor/behave/features/runner.tag_logic.feature +67 -0
- data/vendor/behave/features/runner.unknown_formatter.feature +23 -0
- data/vendor/behave/features/runner.use_stage_implementations.feature +126 -0
- data/vendor/behave/features/scenario.description.feature +171 -0
- data/vendor/behave/features/scenario.exclude_from_run.feature +217 -0
- data/vendor/behave/features/scenario_outline.basics.feature +100 -0
- data/vendor/behave/features/scenario_outline.improved.feature +177 -0
- data/vendor/behave/features/scenario_outline.name_annotation.feature +157 -0
- data/vendor/behave/features/scenario_outline.parametrized.feature +401 -0
- data/vendor/behave/features/scenario_outline.tagged_examples.feature +118 -0
- data/vendor/behave/features/step.async_steps.feature +225 -0
- data/vendor/behave/features/step.duplicated_step.feature +106 -0
- data/vendor/behave/features/step.execute_steps.feature +59 -0
- data/vendor/behave/features/step.execute_steps.with_table.feature +65 -0
- data/vendor/behave/features/step.import_other_step_module.feature +103 -0
- data/vendor/behave/features/step.pending_steps.feature +128 -0
- data/vendor/behave/features/step.undefined_steps.feature +307 -0
- data/vendor/behave/features/step.use_step_library.feature +44 -0
- data/vendor/behave/features/step_dialect.generic_steps.feature +189 -0
- data/vendor/behave/features/step_dialect.given_when_then.feature +89 -0
- data/vendor/behave/features/step_param.builtin_types.with_float.feature +239 -0
- data/vendor/behave/features/step_param.builtin_types.with_integer.feature +305 -0
- data/vendor/behave/features/step_param.custom_types.feature +134 -0
- data/vendor/behave/features/steps/behave_active_tags_steps.py +86 -0
- data/vendor/behave/features/steps/behave_context_steps.py +67 -0
- data/vendor/behave/features/steps/behave_model_tag_logic_steps.py +105 -0
- data/vendor/behave/features/steps/behave_model_util.py +105 -0
- data/vendor/behave/features/steps/behave_select_files_steps.py +83 -0
- data/vendor/behave/features/steps/behave_tag_expression_steps.py +166 -0
- data/vendor/behave/features/steps/behave_undefined_steps.py +101 -0
- data/vendor/behave/features/steps/use_steplib_behave4cmd.py +12 -0
- data/vendor/behave/features/summary.undefined_steps.feature +114 -0
- data/vendor/behave/features/tags.active_tags.feature +385 -0
- data/vendor/behave/features/tags.default_tags.feature +104 -0
- data/vendor/behave/features/tags.tag_expression.feature +105 -0
- data/vendor/behave/features/userdata.feature +331 -0
- data/vendor/behave/invoke.yaml +21 -0
- data/vendor/behave/issue.features/README.txt +17 -0
- data/vendor/behave/issue.features/environment.py +97 -0
- data/vendor/behave/issue.features/issue0030.feature +21 -0
- data/vendor/behave/issue.features/issue0031.feature +16 -0
- data/vendor/behave/issue.features/issue0032.feature +28 -0
- data/vendor/behave/issue.features/issue0035.feature +74 -0
- data/vendor/behave/issue.features/issue0040.feature +154 -0
- data/vendor/behave/issue.features/issue0041.feature +135 -0
- data/vendor/behave/issue.features/issue0042.feature +230 -0
- data/vendor/behave/issue.features/issue0044.feature +51 -0
- data/vendor/behave/issue.features/issue0046.feature +77 -0
- data/vendor/behave/issue.features/issue0052.feature +66 -0
- data/vendor/behave/issue.features/issue0059.feature +29 -0
- data/vendor/behave/issue.features/issue0063.feature +102 -0
- data/vendor/behave/issue.features/issue0064.feature +97 -0
- data/vendor/behave/issue.features/issue0065.feature +18 -0
- data/vendor/behave/issue.features/issue0066.feature +80 -0
- data/vendor/behave/issue.features/issue0067.feature +90 -0
- data/vendor/behave/issue.features/issue0069.feature +64 -0
- data/vendor/behave/issue.features/issue0072.feature +32 -0
- data/vendor/behave/issue.features/issue0073.feature +228 -0
- data/vendor/behave/issue.features/issue0075.feature +18 -0
- data/vendor/behave/issue.features/issue0077.feature +89 -0
- data/vendor/behave/issue.features/issue0080.feature +49 -0
- data/vendor/behave/issue.features/issue0081.feature +138 -0
- data/vendor/behave/issue.features/issue0083.feature +69 -0
- data/vendor/behave/issue.features/issue0084.feature +69 -0
- data/vendor/behave/issue.features/issue0085.feature +119 -0
- data/vendor/behave/issue.features/issue0092.feature +66 -0
- data/vendor/behave/issue.features/issue0096.feature +173 -0
- data/vendor/behave/issue.features/issue0099.feature +130 -0
- data/vendor/behave/issue.features/issue0109.feature +60 -0
- data/vendor/behave/issue.features/issue0111.feature +53 -0
- data/vendor/behave/issue.features/issue0112.feature +64 -0
- data/vendor/behave/issue.features/issue0114.feature +118 -0
- data/vendor/behave/issue.features/issue0116.feature +71 -0
- data/vendor/behave/issue.features/issue0125.feature +49 -0
- data/vendor/behave/issue.features/issue0127.feature +64 -0
- data/vendor/behave/issue.features/issue0139.feature +67 -0
- data/vendor/behave/issue.features/issue0142.feature +37 -0
- data/vendor/behave/issue.features/issue0143.feature +54 -0
- data/vendor/behave/issue.features/issue0145.feature +63 -0
- data/vendor/behave/issue.features/issue0148.feature +105 -0
- data/vendor/behave/issue.features/issue0152.feature +52 -0
- data/vendor/behave/issue.features/issue0159.feature +74 -0
- data/vendor/behave/issue.features/issue0162.feature +86 -0
- data/vendor/behave/issue.features/issue0171.feature +16 -0
- data/vendor/behave/issue.features/issue0172.feature +51 -0
- data/vendor/behave/issue.features/issue0175.feature +91 -0
- data/vendor/behave/issue.features/issue0177.feature +40 -0
- data/vendor/behave/issue.features/issue0181.feature +36 -0
- data/vendor/behave/issue.features/issue0184.feature +144 -0
- data/vendor/behave/issue.features/issue0186.feature +12 -0
- data/vendor/behave/issue.features/issue0188.feature +60 -0
- data/vendor/behave/issue.features/issue0191.feature +178 -0
- data/vendor/behave/issue.features/issue0194.feature +215 -0
- data/vendor/behave/issue.features/issue0197.feature +11 -0
- data/vendor/behave/issue.features/issue0216.feature +129 -0
- data/vendor/behave/issue.features/issue0226.feature +51 -0
- data/vendor/behave/issue.features/issue0228.feature +41 -0
- data/vendor/behave/issue.features/issue0230.feature +46 -0
- data/vendor/behave/issue.features/issue0231.feature +77 -0
- data/vendor/behave/issue.features/issue0238.feature +52 -0
- data/vendor/behave/issue.features/issue0251.feature +15 -0
- data/vendor/behave/issue.features/issue0280.feature +118 -0
- data/vendor/behave/issue.features/issue0288.feature +95 -0
- data/vendor/behave/issue.features/issue0300.feature +49 -0
- data/vendor/behave/issue.features/issue0302.feature +91 -0
- data/vendor/behave/issue.features/issue0309.feature +52 -0
- data/vendor/behave/issue.features/issue0330.feature +124 -0
- data/vendor/behave/issue.features/issue0349.feature +9 -0
- data/vendor/behave/issue.features/issue0361.feature +79 -0
- data/vendor/behave/issue.features/issue0383.feature +76 -0
- data/vendor/behave/issue.features/issue0384.feature +103 -0
- data/vendor/behave/issue.features/issue0385.feature +109 -0
- data/vendor/behave/issue.features/issue0424.feature +66 -0
- data/vendor/behave/issue.features/issue0446.feature +116 -0
- data/vendor/behave/issue.features/issue0449.feature +42 -0
- data/vendor/behave/issue.features/issue0453.feature +42 -0
- data/vendor/behave/issue.features/issue0457.feature +65 -0
- data/vendor/behave/issue.features/issue0462.feature +38 -0
- data/vendor/behave/issue.features/issue0476.feature +39 -0
- data/vendor/behave/issue.features/issue0487.feature +92 -0
- data/vendor/behave/issue.features/issue0506.feature +77 -0
- data/vendor/behave/issue.features/issue0510.feature +51 -0
- data/vendor/behave/issue.features/requirements.txt +12 -0
- data/vendor/behave/issue.features/steps/ansi_steps.py +20 -0
- data/vendor/behave/issue.features/steps/behave_hooks_steps.py +10 -0
- data/vendor/behave/issue.features/steps/use_steplib_behave4cmd.py +13 -0
- data/vendor/behave/more.features/formatter.json.validate_output.feature +37 -0
- data/vendor/behave/more.features/steps/tutorial_steps.py +16 -0
- data/vendor/behave/more.features/steps/use_steplib_behave4cmd.py +7 -0
- data/vendor/behave/more.features/tutorial.feature +6 -0
- data/vendor/behave/py.requirements/README.txt +5 -0
- data/vendor/behave/py.requirements/all.txt +16 -0
- data/vendor/behave/py.requirements/basic.txt +21 -0
- data/vendor/behave/py.requirements/develop.txt +28 -0
- data/vendor/behave/py.requirements/docs.txt +6 -0
- data/vendor/behave/py.requirements/json.txt +7 -0
- data/vendor/behave/py.requirements/more_py26.txt +8 -0
- data/vendor/behave/py.requirements/testing.txt +10 -0
- data/vendor/behave/pytest.ini +24 -0
- data/vendor/behave/setup.cfg +29 -0
- data/vendor/behave/setup.py +118 -0
- data/vendor/behave/setuptools_behave.py +130 -0
- data/vendor/behave/tasks/__behave.py +45 -0
- data/vendor/behave/tasks/__init__.py +55 -0
- data/vendor/behave/tasks/__main__.py +70 -0
- data/vendor/behave/tasks/_setup.py +135 -0
- data/vendor/behave/tasks/_vendor/README.rst +35 -0
- data/vendor/behave/tasks/_vendor/invoke.zip +0 -0
- data/vendor/behave/tasks/_vendor/path.py +1725 -0
- data/vendor/behave/tasks/_vendor/pathlib.py +1280 -0
- data/vendor/behave/tasks/_vendor/six.py +868 -0
- data/vendor/behave/tasks/clean.py +246 -0
- data/vendor/behave/tasks/docs.py +97 -0
- data/vendor/behave/tasks/requirements.txt +17 -0
- data/vendor/behave/tasks/test.py +192 -0
- data/vendor/behave/test/__init__.py +0 -0
- data/vendor/behave/test/_importer_candidate.py +3 -0
- data/vendor/behave/test/reporters/__init__.py +0 -0
- data/vendor/behave/test/reporters/test_summary.py +240 -0
- data/vendor/behave/test/test_ansi_escapes.py +73 -0
- data/vendor/behave/test/test_configuration.py +172 -0
- data/vendor/behave/test/test_formatter.py +265 -0
- data/vendor/behave/test/test_formatter_progress.py +39 -0
- data/vendor/behave/test/test_formatter_rerun.py +97 -0
- data/vendor/behave/test/test_formatter_tags.py +57 -0
- data/vendor/behave/test/test_importer.py +151 -0
- data/vendor/behave/test/test_log_capture.py +29 -0
- data/vendor/behave/test/test_matchers.py +236 -0
- data/vendor/behave/test/test_model.py +871 -0
- data/vendor/behave/test/test_parser.py +1590 -0
- data/vendor/behave/test/test_runner.py +1074 -0
- data/vendor/behave/test/test_step_registry.py +96 -0
- data/vendor/behave/test/test_tag_expression.py +506 -0
- data/vendor/behave/test/test_tag_expression2.py +462 -0
- data/vendor/behave/test/test_tag_matcher.py +729 -0
- data/vendor/behave/test/test_userdata.py +184 -0
- data/vendor/behave/tests/README.txt +12 -0
- data/vendor/behave/tests/__init__.py +0 -0
- data/vendor/behave/tests/api/__ONLY_PY34_or_newer.txt +0 -0
- data/vendor/behave/tests/api/__init__.py +0 -0
- data/vendor/behave/tests/api/_test_async_step34.py +130 -0
- data/vendor/behave/tests/api/_test_async_step35.py +75 -0
- data/vendor/behave/tests/api/test_async_step.py +18 -0
- data/vendor/behave/tests/api/testing_support.py +94 -0
- data/vendor/behave/tests/api/testing_support_async.py +21 -0
- data/vendor/behave/tests/issues/test_issue0336.py +66 -0
- data/vendor/behave/tests/issues/test_issue0449.py +55 -0
- data/vendor/behave/tests/issues/test_issue0453.py +62 -0
- data/vendor/behave/tests/issues/test_issue0458.py +54 -0
- data/vendor/behave/tests/issues/test_issue0495.py +65 -0
- data/vendor/behave/tests/unit/__init__.py +0 -0
- data/vendor/behave/tests/unit/test_behave4cmd_command_shell_proc.py +135 -0
- data/vendor/behave/tests/unit/test_capture.py +280 -0
- data/vendor/behave/tests/unit/test_model_core.py +56 -0
- data/vendor/behave/tests/unit/test_textutil.py +267 -0
- data/vendor/behave/tools/test-features/background.feature +9 -0
- data/vendor/behave/tools/test-features/environment.py +8 -0
- data/vendor/behave/tools/test-features/french.feature +11 -0
- data/vendor/behave/tools/test-features/outline.feature +39 -0
- data/vendor/behave/tools/test-features/parse.feature +10 -0
- data/vendor/behave/tools/test-features/step-data.feature +60 -0
- data/vendor/behave/tools/test-features/steps/steps.py +120 -0
- data/vendor/behave/tools/test-features/tags.feature +18 -0
- data/vendor/behave/tox.ini +159 -0
- metadata +562 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Provides a summary after each test run.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import absolute_import, division
|
|
7
|
+
import sys
|
|
8
|
+
from behave.model import ScenarioOutline
|
|
9
|
+
from behave.model_core import Status
|
|
10
|
+
from behave.reporter.base import Reporter
|
|
11
|
+
from behave.formatter.base import StreamOpener
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# -- DISABLED: optional_steps = ('untested', 'undefined')
|
|
15
|
+
optional_steps = (Status.untested,) # MAYBE: Status.undefined
|
|
16
|
+
status_order = (Status.passed, Status.failed, Status.skipped,
|
|
17
|
+
Status.undefined, Status.untested)
|
|
18
|
+
|
|
19
|
+
def format_summary(statement_type, summary):
|
|
20
|
+
parts = []
|
|
21
|
+
for status in status_order:
|
|
22
|
+
if status.name not in summary:
|
|
23
|
+
continue
|
|
24
|
+
counts = summary[status.name]
|
|
25
|
+
if status in optional_steps and counts == 0:
|
|
26
|
+
# -- SHOW-ONLY: For relevant counts, suppress: untested items, etc.
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
if not parts:
|
|
30
|
+
# -- FIRST ITEM: Add statement_type to counter.
|
|
31
|
+
label = statement_type
|
|
32
|
+
if counts != 1:
|
|
33
|
+
label += 's'
|
|
34
|
+
part = u"%d %s %s" % (counts, label, status.name)
|
|
35
|
+
else:
|
|
36
|
+
part = u"%d %s" % (counts, status.name)
|
|
37
|
+
parts.append(part)
|
|
38
|
+
return ", ".join(parts) + "\n"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SummaryReporter(Reporter):
|
|
42
|
+
show_failed_scenarios = True
|
|
43
|
+
output_stream_name = "stdout"
|
|
44
|
+
|
|
45
|
+
def __init__(self, config):
|
|
46
|
+
super(SummaryReporter, self).__init__(config)
|
|
47
|
+
stream = getattr(sys, self.output_stream_name, sys.stderr)
|
|
48
|
+
self.stream = StreamOpener.ensure_stream_with_encoder(stream)
|
|
49
|
+
self.feature_summary = {Status.passed.name: 0, Status.failed.name: 0,
|
|
50
|
+
Status.skipped.name: 0, Status.untested.name: 0}
|
|
51
|
+
self.scenario_summary = {Status.passed.name: 0, Status.failed.name: 0,
|
|
52
|
+
Status.skipped.name: 0, Status.untested.name: 0}
|
|
53
|
+
self.step_summary = {Status.passed.name: 0, Status.failed.name: 0,
|
|
54
|
+
Status.skipped.name: 0, Status.untested.name: 0,
|
|
55
|
+
Status.undefined.name: 0}
|
|
56
|
+
self.duration = 0.0
|
|
57
|
+
self.failed_scenarios = []
|
|
58
|
+
|
|
59
|
+
def feature(self, feature):
|
|
60
|
+
self.feature_summary[feature.status.name] += 1
|
|
61
|
+
self.duration += feature.duration
|
|
62
|
+
for scenario in feature:
|
|
63
|
+
if isinstance(scenario, ScenarioOutline):
|
|
64
|
+
self.process_scenario_outline(scenario)
|
|
65
|
+
else:
|
|
66
|
+
self.process_scenario(scenario)
|
|
67
|
+
|
|
68
|
+
def end(self):
|
|
69
|
+
# -- SHOW FAILED SCENARIOS (optional):
|
|
70
|
+
if self.show_failed_scenarios and self.failed_scenarios:
|
|
71
|
+
self.stream.write("\nFailing scenarios:\n")
|
|
72
|
+
for scenario in self.failed_scenarios:
|
|
73
|
+
self.stream.write(u" %s %s\n" % (
|
|
74
|
+
scenario.location, scenario.name))
|
|
75
|
+
self.stream.write("\n")
|
|
76
|
+
|
|
77
|
+
# -- SHOW SUMMARY COUNTS:
|
|
78
|
+
self.stream.write(format_summary("feature", self.feature_summary))
|
|
79
|
+
self.stream.write(format_summary("scenario", self.scenario_summary))
|
|
80
|
+
self.stream.write(format_summary("step", self.step_summary))
|
|
81
|
+
timings = (int(self.duration / 60.0), self.duration % 60)
|
|
82
|
+
self.stream.write('Took %dm%02.3fs\n' % timings)
|
|
83
|
+
|
|
84
|
+
def process_scenario(self, scenario):
|
|
85
|
+
if scenario.status == Status.failed:
|
|
86
|
+
self.failed_scenarios.append(scenario)
|
|
87
|
+
|
|
88
|
+
self.scenario_summary[scenario.status.name] += 1
|
|
89
|
+
for step in scenario:
|
|
90
|
+
self.step_summary[step.status.name] += 1
|
|
91
|
+
|
|
92
|
+
def process_scenario_outline(self, scenario_outline):
|
|
93
|
+
for scenario in scenario_outline.scenarios:
|
|
94
|
+
self.process_scenario(scenario)
|
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
This module provides Runner class to run behave feature files (or model elements).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import absolute_import, print_function, with_statement
|
|
7
|
+
import contextlib
|
|
8
|
+
import os.path
|
|
9
|
+
import sys
|
|
10
|
+
import warnings
|
|
11
|
+
import weakref
|
|
12
|
+
import six
|
|
13
|
+
from behave import matchers
|
|
14
|
+
from behave.step_registry import setup_step_decorators, registry as the_step_registry
|
|
15
|
+
from behave.formatter._registry import make_formatters
|
|
16
|
+
from behave.configuration import ConfigError
|
|
17
|
+
from behave.capture import CaptureController
|
|
18
|
+
from behave.runner_util import collect_feature_locations, parse_features
|
|
19
|
+
from behave._types import ExceptionUtil
|
|
20
|
+
if six.PY2:
|
|
21
|
+
# -- USE PYTHON3 BACKPORT: With unicode traceback support.
|
|
22
|
+
import traceback2 as traceback
|
|
23
|
+
else:
|
|
24
|
+
import traceback
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ContextMaskWarning(UserWarning):
|
|
29
|
+
"""Raised if a context variable is being overwritten in some situations.
|
|
30
|
+
|
|
31
|
+
If the variable was originally set by user code then this will be raised if
|
|
32
|
+
*behave* overwites the value.
|
|
33
|
+
|
|
34
|
+
If the variable was originally set by *behave* then this will be raised if
|
|
35
|
+
user code overwites the value.
|
|
36
|
+
"""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Context(object):
|
|
41
|
+
"""Hold contextual information during the running of tests.
|
|
42
|
+
|
|
43
|
+
This object is a place to store information related to the tests you're
|
|
44
|
+
running. You may add arbitrary attributes to it of whatever value you need.
|
|
45
|
+
|
|
46
|
+
During the running of your tests the object will have additional layers of
|
|
47
|
+
namespace added and removed automatically. There is a "root" namespace and
|
|
48
|
+
additional namespaces for features and scenarios.
|
|
49
|
+
|
|
50
|
+
Certain names are used by *behave*; be wary of using them yourself as
|
|
51
|
+
*behave* may overwrite the value you set. These names are:
|
|
52
|
+
|
|
53
|
+
.. attribute:: feature
|
|
54
|
+
|
|
55
|
+
This is set when we start testing a new feature and holds a
|
|
56
|
+
:class:`~behave.model.Feature`. It will not be present outside of a
|
|
57
|
+
feature (i.e. within the scope of the environment before_all and
|
|
58
|
+
after_all).
|
|
59
|
+
|
|
60
|
+
.. attribute:: scenario
|
|
61
|
+
|
|
62
|
+
This is set when we start testing a new scenario (including the
|
|
63
|
+
individual scenarios of a scenario outline) and holds a
|
|
64
|
+
:class:`~behave.model.Scenario`. It will not be present outside of the
|
|
65
|
+
scope of a scenario.
|
|
66
|
+
|
|
67
|
+
.. attribute:: tags
|
|
68
|
+
|
|
69
|
+
The current set of active tags (as a Python set containing instances of
|
|
70
|
+
:class:`~behave.model.Tag` which are basically just glorified strings)
|
|
71
|
+
combined from the feature and scenario. This attribute will not be
|
|
72
|
+
present outside of a feature scope.
|
|
73
|
+
|
|
74
|
+
.. attribute:: aborted
|
|
75
|
+
|
|
76
|
+
This is set to true in the root namespace when the user aborts a test run
|
|
77
|
+
(:exc:`KeyboardInterrupt` exception). Initially: False.
|
|
78
|
+
|
|
79
|
+
.. attribute:: failed
|
|
80
|
+
|
|
81
|
+
This is set to true in the root namespace as soon as a step fails.
|
|
82
|
+
Initially: False.
|
|
83
|
+
|
|
84
|
+
.. attribute:: table
|
|
85
|
+
|
|
86
|
+
This is set at the step level and holds any :class:`~behave.model.Table`
|
|
87
|
+
associated with the step.
|
|
88
|
+
|
|
89
|
+
.. attribute:: text
|
|
90
|
+
|
|
91
|
+
This is set at the step level and holds any multiline text associated
|
|
92
|
+
with the step.
|
|
93
|
+
|
|
94
|
+
.. attribute:: config
|
|
95
|
+
|
|
96
|
+
The configuration of *behave* as determined by configuration files and
|
|
97
|
+
command-line options. The attributes of this object are the same as the
|
|
98
|
+
`configuration file section names`_.
|
|
99
|
+
|
|
100
|
+
.. attribute:: active_outline
|
|
101
|
+
|
|
102
|
+
This is set for each scenario in a scenario outline and references the
|
|
103
|
+
:class:`~behave.model.Row` that is active for the current scenario. It is
|
|
104
|
+
present mostly for debugging, but may be useful otherwise.
|
|
105
|
+
|
|
106
|
+
.. attribute:: log_capture
|
|
107
|
+
|
|
108
|
+
If logging capture is enabled then this attribute contains the captured
|
|
109
|
+
logging as an instance of :class:`~behave.log_capture.LoggingCapture`.
|
|
110
|
+
It is not present if logging is not being captured.
|
|
111
|
+
|
|
112
|
+
.. attribute:: stdout_capture
|
|
113
|
+
|
|
114
|
+
If stdout capture is enabled then this attribute contains the captured
|
|
115
|
+
output as a StringIO instance. It is not present if stdout is not being
|
|
116
|
+
captured.
|
|
117
|
+
|
|
118
|
+
.. attribute:: stderr_capture
|
|
119
|
+
|
|
120
|
+
If stderr capture is enabled then this attribute contains the captured
|
|
121
|
+
output as a StringIO instance. It is not present if stderr is not being
|
|
122
|
+
captured.
|
|
123
|
+
|
|
124
|
+
If an attempt made by user code to overwrite one of these variables, or
|
|
125
|
+
indeed by *behave* to overwite a user-set variable, then a
|
|
126
|
+
:class:`behave.runner.ContextMaskWarning` warning will be raised.
|
|
127
|
+
|
|
128
|
+
You may use the "in" operator to test whether a certain value has been set
|
|
129
|
+
on the context, for example:
|
|
130
|
+
|
|
131
|
+
"feature" in context
|
|
132
|
+
|
|
133
|
+
checks whether there is a "feature" value in the context.
|
|
134
|
+
|
|
135
|
+
Values may be deleted from the context using "del" but only at the level
|
|
136
|
+
they are set. You can't delete a value set by a feature at a scenario level
|
|
137
|
+
but you can delete a value set for a scenario in that scenario.
|
|
138
|
+
|
|
139
|
+
.. _`configuration file section names`: behave.html#configuration-files
|
|
140
|
+
"""
|
|
141
|
+
# pylint: disable=too-many-instance-attributes
|
|
142
|
+
BEHAVE = "behave"
|
|
143
|
+
USER = "user"
|
|
144
|
+
|
|
145
|
+
def __init__(self, runner):
|
|
146
|
+
self._runner = weakref.proxy(runner)
|
|
147
|
+
self._config = runner.config
|
|
148
|
+
d = self._root = {
|
|
149
|
+
"aborted": False,
|
|
150
|
+
"failed": False,
|
|
151
|
+
"config": self._config,
|
|
152
|
+
"active_outline": None,
|
|
153
|
+
}
|
|
154
|
+
self._stack = [d]
|
|
155
|
+
self._record = {}
|
|
156
|
+
self._origin = {}
|
|
157
|
+
self._mode = self.BEHAVE
|
|
158
|
+
self.feature = None
|
|
159
|
+
# -- RECHECK: If needed
|
|
160
|
+
self.text = None
|
|
161
|
+
self.table = None
|
|
162
|
+
self.stdout_capture = None
|
|
163
|
+
self.stderr_capture = None
|
|
164
|
+
self.log_capture = None
|
|
165
|
+
|
|
166
|
+
def _push(self):
|
|
167
|
+
self._stack.insert(0, {})
|
|
168
|
+
|
|
169
|
+
def _pop(self):
|
|
170
|
+
self._stack.pop(0)
|
|
171
|
+
|
|
172
|
+
def _use_with_behave_mode(self):
|
|
173
|
+
"""Provides a context manager for using the context in BEHAVE mode."""
|
|
174
|
+
return use_context_with_mode(self, Context.BEHAVE)
|
|
175
|
+
|
|
176
|
+
def use_with_user_mode(self):
|
|
177
|
+
"""Provides a context manager for using the context in USER mode."""
|
|
178
|
+
return use_context_with_mode(self, Context.USER)
|
|
179
|
+
|
|
180
|
+
def user_mode(self):
|
|
181
|
+
warnings.warn("Use 'use_with_user_mode()' instead",
|
|
182
|
+
PendingDeprecationWarning, stacklevel=2)
|
|
183
|
+
return self.use_with_user_mode()
|
|
184
|
+
|
|
185
|
+
def _set_root_attribute(self, attr, value):
|
|
186
|
+
for frame in self.__dict__["_stack"]:
|
|
187
|
+
if frame is self.__dict__["_root"]:
|
|
188
|
+
continue
|
|
189
|
+
if attr in frame:
|
|
190
|
+
record = self.__dict__["_record"][attr]
|
|
191
|
+
params = {
|
|
192
|
+
"attr": attr,
|
|
193
|
+
"filename": record[0],
|
|
194
|
+
"line": record[1],
|
|
195
|
+
"function": record[3],
|
|
196
|
+
}
|
|
197
|
+
self._emit_warning(attr, params)
|
|
198
|
+
|
|
199
|
+
self.__dict__["_root"][attr] = value
|
|
200
|
+
if attr not in self._origin:
|
|
201
|
+
self._origin[attr] = self._mode
|
|
202
|
+
|
|
203
|
+
def _emit_warning(self, attr, params):
|
|
204
|
+
msg = ""
|
|
205
|
+
if self._mode is self.BEHAVE and self._origin[attr] is not self.BEHAVE:
|
|
206
|
+
msg = "behave runner is masking context attribute '%(attr)s' " \
|
|
207
|
+
"originally set in %(function)s (%(filename)s:%(line)s)"
|
|
208
|
+
elif self._mode is self.USER:
|
|
209
|
+
if self._origin[attr] is not self.USER:
|
|
210
|
+
msg = "user code is masking context attribute '%(attr)s' " \
|
|
211
|
+
"originally set by behave"
|
|
212
|
+
elif self._config.verbose:
|
|
213
|
+
msg = "user code is masking context attribute " \
|
|
214
|
+
"'%(attr)s'; see the tutorial for what this means"
|
|
215
|
+
if msg:
|
|
216
|
+
msg = msg % params
|
|
217
|
+
warnings.warn(msg, ContextMaskWarning, stacklevel=3)
|
|
218
|
+
|
|
219
|
+
def _dump(self, pretty=False, prefix=" "):
|
|
220
|
+
for level, frame in enumerate(self._stack):
|
|
221
|
+
print("%sLevel %d" % (prefix, level))
|
|
222
|
+
if pretty:
|
|
223
|
+
for name in sorted(frame.keys()):
|
|
224
|
+
value = frame[name]
|
|
225
|
+
print("%s %-15s = %r" % (prefix, name, value))
|
|
226
|
+
else:
|
|
227
|
+
print(prefix + repr(frame))
|
|
228
|
+
|
|
229
|
+
def __getattr__(self, attr):
|
|
230
|
+
if attr[0] == "_":
|
|
231
|
+
return self.__dict__[attr]
|
|
232
|
+
for frame in self._stack:
|
|
233
|
+
if attr in frame:
|
|
234
|
+
return frame[attr]
|
|
235
|
+
msg = "'{0}' object has no attribute '{1}'"
|
|
236
|
+
msg = msg.format(self.__class__.__name__, attr)
|
|
237
|
+
raise AttributeError(msg)
|
|
238
|
+
|
|
239
|
+
def __setattr__(self, attr, value):
|
|
240
|
+
if attr[0] == "_":
|
|
241
|
+
self.__dict__[attr] = value
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
for frame in self._stack[1:]:
|
|
245
|
+
if attr in frame:
|
|
246
|
+
record = self._record[attr]
|
|
247
|
+
params = {
|
|
248
|
+
"attr": attr,
|
|
249
|
+
"filename": record[0],
|
|
250
|
+
"line": record[1],
|
|
251
|
+
"function": record[3],
|
|
252
|
+
}
|
|
253
|
+
self._emit_warning(attr, params)
|
|
254
|
+
|
|
255
|
+
stack_limit = 2
|
|
256
|
+
if six.PY2:
|
|
257
|
+
stack_limit += 1 # Due to traceback2 usage.
|
|
258
|
+
stack_frame = traceback.extract_stack(limit=stack_limit)[0]
|
|
259
|
+
self._record[attr] = stack_frame
|
|
260
|
+
frame = self._stack[0]
|
|
261
|
+
frame[attr] = value
|
|
262
|
+
if attr not in self._origin:
|
|
263
|
+
self._origin[attr] = self._mode
|
|
264
|
+
|
|
265
|
+
def __delattr__(self, attr):
|
|
266
|
+
frame = self._stack[0]
|
|
267
|
+
if attr in frame:
|
|
268
|
+
del frame[attr]
|
|
269
|
+
del self._record[attr]
|
|
270
|
+
else:
|
|
271
|
+
msg = "'{0}' object has no attribute '{1}' at the current level"
|
|
272
|
+
msg = msg.format(self.__class__.__name__, attr)
|
|
273
|
+
raise AttributeError(msg)
|
|
274
|
+
|
|
275
|
+
def __contains__(self, attr):
|
|
276
|
+
if attr[0] == "_":
|
|
277
|
+
return attr in self.__dict__
|
|
278
|
+
for frame in self._stack:
|
|
279
|
+
if attr in frame:
|
|
280
|
+
return True
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
def execute_steps(self, steps_text):
|
|
284
|
+
"""The steps identified in the "steps" text string will be parsed and
|
|
285
|
+
executed in turn just as though they were defined in a feature file.
|
|
286
|
+
|
|
287
|
+
If the execute_steps call fails (either through error or failure
|
|
288
|
+
assertion) then the step invoking it will fail.
|
|
289
|
+
|
|
290
|
+
ValueError will be raised if this is invoked outside a feature context.
|
|
291
|
+
|
|
292
|
+
Returns boolean False if the steps are not parseable, True otherwise.
|
|
293
|
+
"""
|
|
294
|
+
assert isinstance(steps_text, six.text_type), "Steps must be unicode."
|
|
295
|
+
if not self.feature:
|
|
296
|
+
raise ValueError("execute_steps() called outside of feature")
|
|
297
|
+
|
|
298
|
+
# -- PREPARE: Save original context data for current step.
|
|
299
|
+
# Needed if step definition that called this method uses .table/.text
|
|
300
|
+
original_table = getattr(self, "table", None)
|
|
301
|
+
original_text = getattr(self, "text", None)
|
|
302
|
+
|
|
303
|
+
self.feature.parser.variant = "steps"
|
|
304
|
+
steps = self.feature.parser.parse_steps(steps_text)
|
|
305
|
+
with self._use_with_behave_mode():
|
|
306
|
+
for step in steps:
|
|
307
|
+
passed = step.run(self._runner, quiet=True, capture=False)
|
|
308
|
+
if not passed:
|
|
309
|
+
# -- ISSUE #96: Provide more substep info to diagnose problem.
|
|
310
|
+
step_line = u"%s %s" % (step.keyword, step.name)
|
|
311
|
+
message = "%s SUB-STEP: %s" % \
|
|
312
|
+
(step.status.name.upper(), step_line)
|
|
313
|
+
if step.error_message:
|
|
314
|
+
message += "\nSubstep info: %s\n" % step.error_message
|
|
315
|
+
message += u"Traceback (of failed substep):\n"
|
|
316
|
+
message += u"".join(traceback.format_tb(step.exc_traceback))
|
|
317
|
+
# message += u"\nTraceback (of context.execute_steps()):"
|
|
318
|
+
assert False, message
|
|
319
|
+
|
|
320
|
+
# -- FINALLY: Restore original context data for current step.
|
|
321
|
+
self.table = original_table
|
|
322
|
+
self.text = original_text
|
|
323
|
+
return True
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@contextlib.contextmanager
|
|
327
|
+
def use_context_with_mode(context, mode):
|
|
328
|
+
"""Switch context to BEHAVE or USER mode.
|
|
329
|
+
Provides a context manager for switching between the two context modes.
|
|
330
|
+
|
|
331
|
+
.. sourcecode:: python
|
|
332
|
+
|
|
333
|
+
context = Context()
|
|
334
|
+
with use_context_with_mode(context, Context.BEHAVE):
|
|
335
|
+
... # Do something
|
|
336
|
+
# -- POSTCONDITION: Original context._mode is restored.
|
|
337
|
+
|
|
338
|
+
:param context: Context object to use.
|
|
339
|
+
:param mode: Mode to apply to context object.
|
|
340
|
+
"""
|
|
341
|
+
# pylint: disable=protected-access
|
|
342
|
+
assert mode in (Context.BEHAVE, Context.USER)
|
|
343
|
+
current_mode = context._mode
|
|
344
|
+
try:
|
|
345
|
+
context._mode = mode
|
|
346
|
+
yield
|
|
347
|
+
finally:
|
|
348
|
+
# -- RESTORE: Initial current_mode
|
|
349
|
+
# Even if an AssertionError/Exception is raised.
|
|
350
|
+
context._mode = current_mode
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def exec_file(filename, globals_=None, locals_=None):
|
|
355
|
+
if globals_ is None:
|
|
356
|
+
globals_ = {}
|
|
357
|
+
if locals_ is None:
|
|
358
|
+
locals_ = globals_
|
|
359
|
+
locals_["__file__"] = filename
|
|
360
|
+
with open(filename, "rb") as f:
|
|
361
|
+
# pylint: disable=exec-used
|
|
362
|
+
filename2 = os.path.relpath(filename, os.getcwd())
|
|
363
|
+
code = compile(f.read(), filename2, "exec", dont_inherit=True)
|
|
364
|
+
exec(code, globals_, locals_)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def path_getrootdir(path):
|
|
368
|
+
"""
|
|
369
|
+
Extract rootdir from path in a platform independent way.
|
|
370
|
+
|
|
371
|
+
POSIX-PATH EXAMPLE:
|
|
372
|
+
rootdir = path_getrootdir("/foo/bar/one.feature")
|
|
373
|
+
assert rootdir == "/"
|
|
374
|
+
|
|
375
|
+
WINDOWS-PATH EXAMPLE:
|
|
376
|
+
rootdir = path_getrootdir("D:\\foo\\bar\\one.feature")
|
|
377
|
+
assert rootdir == r"D:\"
|
|
378
|
+
"""
|
|
379
|
+
drive, _ = os.path.splitdrive(path)
|
|
380
|
+
if drive:
|
|
381
|
+
# -- WINDOWS:
|
|
382
|
+
return drive + os.path.sep
|
|
383
|
+
# -- POSIX:
|
|
384
|
+
return os.path.sep
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class PathManager(object):
|
|
388
|
+
"""
|
|
389
|
+
Context manager to add paths to sys.path (python search path) within a scope
|
|
390
|
+
"""
|
|
391
|
+
def __init__(self, paths=None):
|
|
392
|
+
self.initial_paths = paths or []
|
|
393
|
+
self.paths = None
|
|
394
|
+
|
|
395
|
+
def __enter__(self):
|
|
396
|
+
self.paths = list(self.initial_paths)
|
|
397
|
+
sys.path = self.paths + sys.path
|
|
398
|
+
|
|
399
|
+
def __exit__(self, *crap):
|
|
400
|
+
for path in self.paths:
|
|
401
|
+
sys.path.remove(path)
|
|
402
|
+
self.paths = None
|
|
403
|
+
|
|
404
|
+
def add(self, path):
|
|
405
|
+
if self.paths is None:
|
|
406
|
+
# -- CALLED OUTSIDE OF CONTEXT:
|
|
407
|
+
self.initial_paths.append(path)
|
|
408
|
+
else:
|
|
409
|
+
sys.path.insert(0, path)
|
|
410
|
+
self.paths.append(path)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
class ModelRunner(object):
|
|
414
|
+
"""
|
|
415
|
+
Test runner for a behave model (features).
|
|
416
|
+
Provides the core functionality of a test runner and
|
|
417
|
+
the functional API needed by model elements.
|
|
418
|
+
|
|
419
|
+
.. attribute:: aborted
|
|
420
|
+
|
|
421
|
+
This is set to true when the user aborts a test run
|
|
422
|
+
(:exc:`KeyboardInterrupt` exception). Initially: False.
|
|
423
|
+
Stored as derived attribute in :attr:`Context.aborted`.
|
|
424
|
+
"""
|
|
425
|
+
# pylint: disable=too-many-instance-attributes
|
|
426
|
+
|
|
427
|
+
def __init__(self, config, features=None, step_registry=None):
|
|
428
|
+
self.config = config
|
|
429
|
+
self.features = features or []
|
|
430
|
+
self.hooks = {}
|
|
431
|
+
self.formatters = []
|
|
432
|
+
self.undefined_steps = []
|
|
433
|
+
self.step_registry = step_registry
|
|
434
|
+
self.capture_controller = CaptureController(config)
|
|
435
|
+
|
|
436
|
+
self.context = None
|
|
437
|
+
self.feature = None
|
|
438
|
+
self.hook_failures = 0
|
|
439
|
+
|
|
440
|
+
# @property
|
|
441
|
+
def _get_aborted(self):
|
|
442
|
+
value = False
|
|
443
|
+
if self.context:
|
|
444
|
+
value = self.context.aborted
|
|
445
|
+
return value
|
|
446
|
+
|
|
447
|
+
# @aborted.setter
|
|
448
|
+
def _set_aborted(self, value):
|
|
449
|
+
# pylint: disable=protected-access
|
|
450
|
+
assert self.context, "REQUIRE: context, but context=%r" % self.context
|
|
451
|
+
self.context._set_root_attribute("aborted", bool(value))
|
|
452
|
+
|
|
453
|
+
aborted = property(_get_aborted, _set_aborted,
|
|
454
|
+
doc="Indicates that test run is aborted by the user.")
|
|
455
|
+
|
|
456
|
+
def run_hook(self, name, context, *args):
|
|
457
|
+
if not self.config.dry_run and (name in self.hooks):
|
|
458
|
+
try:
|
|
459
|
+
with context.user_mode():
|
|
460
|
+
self.hooks[name](context, *args)
|
|
461
|
+
# except KeyboardInterrupt:
|
|
462
|
+
# self.aborted = True
|
|
463
|
+
# if name not in ("before_all", "after_all"):
|
|
464
|
+
# raise
|
|
465
|
+
except Exception as e: # pylint: disable=broad-except
|
|
466
|
+
# -- HANDLE HOOK ERRORS:
|
|
467
|
+
use_traceback = False
|
|
468
|
+
if self.config.verbose:
|
|
469
|
+
use_traceback = True
|
|
470
|
+
ExceptionUtil.set_traceback(e)
|
|
471
|
+
extra = u""
|
|
472
|
+
if "tag" in name:
|
|
473
|
+
extra = "(tag=%s)" % args[0]
|
|
474
|
+
|
|
475
|
+
error_text = ExceptionUtil.describe(e, use_traceback).rstrip()
|
|
476
|
+
error_message = u"HOOK-ERROR in %s%s: %s" % (name, extra, error_text)
|
|
477
|
+
print(error_message)
|
|
478
|
+
self.hook_failures += 1
|
|
479
|
+
if "tag" in name:
|
|
480
|
+
# -- SCENARIO or FEATURE
|
|
481
|
+
statement = getattr(context, "scenario", context.feature)
|
|
482
|
+
elif "all" in name:
|
|
483
|
+
# -- ABORT EXECUTION: For before_all/after_all
|
|
484
|
+
self.aborted = True
|
|
485
|
+
statement = None
|
|
486
|
+
else:
|
|
487
|
+
# -- CASE: feature, scenario, step
|
|
488
|
+
statement = args[0]
|
|
489
|
+
|
|
490
|
+
if statement:
|
|
491
|
+
# -- CASE: feature, scenario, step
|
|
492
|
+
statement.hook_failed = True
|
|
493
|
+
if statement.error_message:
|
|
494
|
+
# -- NOTE: One exception/failure is already stored.
|
|
495
|
+
# Append only error message.
|
|
496
|
+
statement.error_message += u"\n"+ error_message
|
|
497
|
+
else:
|
|
498
|
+
# -- FIRST EXCEPTION/FAILURE:
|
|
499
|
+
statement.store_exception_context(e)
|
|
500
|
+
statement.error_message = error_message
|
|
501
|
+
|
|
502
|
+
def setup_capture(self):
|
|
503
|
+
if not self.context:
|
|
504
|
+
self.context = Context(self)
|
|
505
|
+
self.capture_controller.setup_capture(self.context)
|
|
506
|
+
|
|
507
|
+
def start_capture(self):
|
|
508
|
+
self.capture_controller.start_capture()
|
|
509
|
+
|
|
510
|
+
def stop_capture(self):
|
|
511
|
+
self.capture_controller.stop_capture()
|
|
512
|
+
|
|
513
|
+
def teardown_capture(self):
|
|
514
|
+
self.capture_controller.teardown_capture()
|
|
515
|
+
|
|
516
|
+
def run_model(self, features=None):
|
|
517
|
+
# pylint: disable=too-many-branches
|
|
518
|
+
if not self.context:
|
|
519
|
+
self.context = Context(self)
|
|
520
|
+
if self.step_registry is None:
|
|
521
|
+
self.step_registry = the_step_registry
|
|
522
|
+
if features is None:
|
|
523
|
+
features = self.features
|
|
524
|
+
|
|
525
|
+
# -- ENSURE: context.execute_steps() works in weird cases (hooks, ...)
|
|
526
|
+
context = self.context
|
|
527
|
+
self.hook_failures = 0
|
|
528
|
+
self.setup_capture()
|
|
529
|
+
self.run_hook("before_all", context)
|
|
530
|
+
|
|
531
|
+
run_feature = not self.aborted
|
|
532
|
+
failed_count = 0
|
|
533
|
+
undefined_steps_initial_size = len(self.undefined_steps)
|
|
534
|
+
for feature in features:
|
|
535
|
+
if run_feature:
|
|
536
|
+
try:
|
|
537
|
+
self.feature = feature
|
|
538
|
+
for formatter in self.formatters:
|
|
539
|
+
formatter.uri(feature.filename)
|
|
540
|
+
|
|
541
|
+
failed = feature.run(self)
|
|
542
|
+
if failed:
|
|
543
|
+
failed_count += 1
|
|
544
|
+
if self.config.stop or self.aborted:
|
|
545
|
+
# -- FAIL-EARLY: After first failure.
|
|
546
|
+
run_feature = False
|
|
547
|
+
except KeyboardInterrupt:
|
|
548
|
+
self.aborted = True
|
|
549
|
+
failed_count += 1
|
|
550
|
+
run_feature = False
|
|
551
|
+
|
|
552
|
+
# -- ALWAYS: Report run/not-run feature to reporters.
|
|
553
|
+
# REQUIRED-FOR: Summary to keep track of untested features.
|
|
554
|
+
for reporter in self.config.reporters:
|
|
555
|
+
reporter.feature(feature)
|
|
556
|
+
|
|
557
|
+
# -- AFTER-ALL:
|
|
558
|
+
if self.aborted:
|
|
559
|
+
print("\nABORTED: By user.")
|
|
560
|
+
for formatter in self.formatters:
|
|
561
|
+
formatter.close()
|
|
562
|
+
self.run_hook("after_all", self.context)
|
|
563
|
+
for reporter in self.config.reporters:
|
|
564
|
+
reporter.end()
|
|
565
|
+
|
|
566
|
+
failed = ((failed_count > 0) or self.aborted or (self.hook_failures > 0)
|
|
567
|
+
or (len(self.undefined_steps) > undefined_steps_initial_size))
|
|
568
|
+
return failed
|
|
569
|
+
|
|
570
|
+
def run(self):
|
|
571
|
+
"""
|
|
572
|
+
Implements the run method by running the model.
|
|
573
|
+
"""
|
|
574
|
+
self.context = Context(self)
|
|
575
|
+
return self.run_model()
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class Runner(ModelRunner):
|
|
579
|
+
"""
|
|
580
|
+
Standard test runner for behave:
|
|
581
|
+
|
|
582
|
+
* setup paths
|
|
583
|
+
* loads environment hooks
|
|
584
|
+
* loads step definitions
|
|
585
|
+
* select feature files, parses them and creates model (elements)
|
|
586
|
+
"""
|
|
587
|
+
def __init__(self, config):
|
|
588
|
+
super(Runner, self).__init__(config)
|
|
589
|
+
self.path_manager = PathManager()
|
|
590
|
+
self.base_dir = None
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def setup_paths(self):
|
|
594
|
+
# pylint: disable=too-many-branches, too-many-statements
|
|
595
|
+
if self.config.paths:
|
|
596
|
+
if self.config.verbose:
|
|
597
|
+
print("Supplied path:", \
|
|
598
|
+
", ".join('"%s"' % path for path in self.config.paths))
|
|
599
|
+
first_path = self.config.paths[0]
|
|
600
|
+
if hasattr(first_path, "filename"):
|
|
601
|
+
# -- BETTER: isinstance(first_path, FileLocation):
|
|
602
|
+
first_path = first_path.filename
|
|
603
|
+
base_dir = first_path
|
|
604
|
+
if base_dir.startswith("@"):
|
|
605
|
+
# -- USE: behave @features.txt
|
|
606
|
+
base_dir = base_dir[1:]
|
|
607
|
+
file_locations = self.feature_locations()
|
|
608
|
+
if file_locations:
|
|
609
|
+
base_dir = os.path.dirname(file_locations[0].filename)
|
|
610
|
+
base_dir = os.path.abspath(base_dir)
|
|
611
|
+
|
|
612
|
+
# supplied path might be to a feature file
|
|
613
|
+
if os.path.isfile(base_dir):
|
|
614
|
+
if self.config.verbose:
|
|
615
|
+
print("Primary path is to a file so using its directory")
|
|
616
|
+
base_dir = os.path.dirname(base_dir)
|
|
617
|
+
else:
|
|
618
|
+
if self.config.verbose:
|
|
619
|
+
print('Using default path "./features"')
|
|
620
|
+
base_dir = os.path.abspath("features")
|
|
621
|
+
|
|
622
|
+
# Get the root. This is not guaranteed to be "/" because Windows.
|
|
623
|
+
root_dir = path_getrootdir(base_dir)
|
|
624
|
+
new_base_dir = base_dir
|
|
625
|
+
steps_dir = self.config.steps_dir
|
|
626
|
+
environment_file = self.config.environment_file
|
|
627
|
+
|
|
628
|
+
while True:
|
|
629
|
+
if self.config.verbose:
|
|
630
|
+
print("Trying base directory:", new_base_dir)
|
|
631
|
+
|
|
632
|
+
if os.path.isdir(os.path.join(new_base_dir, steps_dir)):
|
|
633
|
+
break
|
|
634
|
+
if os.path.isfile(os.path.join(new_base_dir, environment_file)):
|
|
635
|
+
break
|
|
636
|
+
if new_base_dir == root_dir:
|
|
637
|
+
break
|
|
638
|
+
|
|
639
|
+
new_base_dir = os.path.dirname(new_base_dir)
|
|
640
|
+
|
|
641
|
+
if new_base_dir == root_dir:
|
|
642
|
+
if self.config.verbose:
|
|
643
|
+
if not self.config.paths:
|
|
644
|
+
print('ERROR: Could not find "%s" directory. '\
|
|
645
|
+
'Please specify where to find your features.' % \
|
|
646
|
+
steps_dir)
|
|
647
|
+
else:
|
|
648
|
+
print('ERROR: Could not find "%s" directory in your '\
|
|
649
|
+
'specified path "%s"' % (steps_dir, base_dir))
|
|
650
|
+
|
|
651
|
+
message = 'No %s directory in %r' % (steps_dir, base_dir)
|
|
652
|
+
raise ConfigError(message)
|
|
653
|
+
|
|
654
|
+
base_dir = new_base_dir
|
|
655
|
+
self.config.base_dir = base_dir
|
|
656
|
+
|
|
657
|
+
for dirpath, dirnames, filenames in os.walk(base_dir):
|
|
658
|
+
if [fn for fn in filenames if fn.endswith(".feature")]:
|
|
659
|
+
break
|
|
660
|
+
else:
|
|
661
|
+
if self.config.verbose:
|
|
662
|
+
if not self.config.paths:
|
|
663
|
+
print('ERROR: Could not find any "<name>.feature" files. '\
|
|
664
|
+
'Please specify where to find your features.')
|
|
665
|
+
else:
|
|
666
|
+
print('ERROR: Could not find any "<name>.feature" files '\
|
|
667
|
+
'in your specified path "%s"' % base_dir)
|
|
668
|
+
raise ConfigError('No feature files in %r' % base_dir)
|
|
669
|
+
|
|
670
|
+
self.base_dir = base_dir
|
|
671
|
+
self.path_manager.add(base_dir)
|
|
672
|
+
if not self.config.paths:
|
|
673
|
+
self.config.paths = [base_dir]
|
|
674
|
+
|
|
675
|
+
if base_dir != os.getcwd():
|
|
676
|
+
self.path_manager.add(os.getcwd())
|
|
677
|
+
|
|
678
|
+
def before_all_default_hook(self, context):
|
|
679
|
+
"""
|
|
680
|
+
Default implementation for :func:`before_all()` hook.
|
|
681
|
+
Setup the logging subsystem based on the configuration data.
|
|
682
|
+
"""
|
|
683
|
+
# pylint: disable=no-self-use
|
|
684
|
+
context.config.setup_logging()
|
|
685
|
+
|
|
686
|
+
def load_hooks(self, filename=None):
|
|
687
|
+
filename = filename or self.config.environment_file
|
|
688
|
+
hooks_path = os.path.join(self.base_dir, filename)
|
|
689
|
+
if os.path.exists(hooks_path):
|
|
690
|
+
exec_file(hooks_path, self.hooks)
|
|
691
|
+
|
|
692
|
+
if "before_all" not in self.hooks:
|
|
693
|
+
self.hooks["before_all"] = self.before_all_default_hook
|
|
694
|
+
|
|
695
|
+
def load_step_definitions(self, extra_step_paths=None):
|
|
696
|
+
if extra_step_paths is None:
|
|
697
|
+
extra_step_paths = []
|
|
698
|
+
step_globals = {
|
|
699
|
+
"use_step_matcher": matchers.use_step_matcher,
|
|
700
|
+
"step_matcher": matchers.step_matcher, # -- DEPRECATING
|
|
701
|
+
}
|
|
702
|
+
setup_step_decorators(step_globals)
|
|
703
|
+
|
|
704
|
+
# -- Allow steps to import other stuff from the steps dir
|
|
705
|
+
# NOTE: Default matcher can be overridden in "environment.py" hook.
|
|
706
|
+
steps_dir = os.path.join(self.base_dir, self.config.steps_dir)
|
|
707
|
+
paths = [steps_dir] + list(extra_step_paths)
|
|
708
|
+
with PathManager(paths):
|
|
709
|
+
default_matcher = matchers.current_matcher
|
|
710
|
+
for path in paths:
|
|
711
|
+
for name in sorted(os.listdir(path)):
|
|
712
|
+
if name.endswith(".py"):
|
|
713
|
+
# -- LOAD STEP DEFINITION:
|
|
714
|
+
# Reset to default matcher after each step-definition.
|
|
715
|
+
# A step-definition may change the matcher 0..N times.
|
|
716
|
+
# ENSURE: Each step definition has clean globals.
|
|
717
|
+
# try:
|
|
718
|
+
step_module_globals = step_globals.copy()
|
|
719
|
+
exec_file(os.path.join(path, name), step_module_globals)
|
|
720
|
+
matchers.current_matcher = default_matcher
|
|
721
|
+
# except Exception as e:
|
|
722
|
+
# e_text = _text(e)
|
|
723
|
+
# print("Exception %s: %s" % \
|
|
724
|
+
# (e.__class__.__name__, e_text))
|
|
725
|
+
# raise
|
|
726
|
+
|
|
727
|
+
def feature_locations(self):
|
|
728
|
+
return collect_feature_locations(self.config.paths)
|
|
729
|
+
|
|
730
|
+
def run(self):
|
|
731
|
+
with self.path_manager:
|
|
732
|
+
self.setup_paths()
|
|
733
|
+
return self.run_with_paths()
|
|
734
|
+
|
|
735
|
+
def run_with_paths(self):
|
|
736
|
+
self.context = Context(self)
|
|
737
|
+
self.load_hooks()
|
|
738
|
+
self.load_step_definitions()
|
|
739
|
+
|
|
740
|
+
# -- ENSURE: context.execute_steps() works in weird cases (hooks, ...)
|
|
741
|
+
# self.setup_capture()
|
|
742
|
+
# self.run_hook("before_all", self.context)
|
|
743
|
+
|
|
744
|
+
# -- STEP: Parse all feature files (by using their file location).
|
|
745
|
+
feature_locations = [filename for filename in self.feature_locations()
|
|
746
|
+
if not self.config.exclude(filename)]
|
|
747
|
+
features = parse_features(feature_locations, language=self.config.lang)
|
|
748
|
+
self.features.extend(features)
|
|
749
|
+
|
|
750
|
+
# -- STEP: Run all features.
|
|
751
|
+
stream_openers = self.config.outputs
|
|
752
|
+
self.formatters = make_formatters(self.config, stream_openers)
|
|
753
|
+
return self.run_model()
|