mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-21979: Use behave formatter and "manage.py django" for behave-django
* Use behave formatter from "teamcity" to integrate PyCharm with behave * If behave is 1.2.6 and behave-django installed and Django enabled -- use "manage.py django" instead of plain behave
This commit is contained in:
@@ -57,6 +57,17 @@ def get_what_to_run_by_env(environment):
|
||||
return base_dir, scenarios, what_to_run
|
||||
|
||||
|
||||
def get_location(base_dir, location_file, location_line):
|
||||
"""
|
||||
Generates location that PyCharm resolves to file
|
||||
:param base_dir: base directory to resolve relative path against
|
||||
:param location_file: path to file
|
||||
:param location_line: line number
|
||||
"""
|
||||
my_file = str(location_file).lstrip("/\\")
|
||||
return "file:///{0}:{1}".format(os.path.normpath(os.path.join(base_dir, my_file)), location_line)
|
||||
|
||||
|
||||
class BddRunner(object):
|
||||
"""
|
||||
Extends this class, implement abstract methods and use its API to implement new BDD frameworks.
|
||||
@@ -101,8 +112,7 @@ class BddRunner(object):
|
||||
:param location object with "file" (relative to base_dir) and "line" fields.
|
||||
:return: location in format file:line (as supported in tcmessages)
|
||||
"""
|
||||
my_file = str(location.file).lstrip("/\\")
|
||||
return "file:///{0}:{1}".format(os.path.normpath(os.path.join(self.__base_dir, my_file)), location.line)
|
||||
return get_location(self.__base_dir, location.file, location.line)
|
||||
|
||||
def _test_undefined(self, test_name, location):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
To support django-behave
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def run_as_django_behave(formatter_name, feature_names, scenario_n_options):
|
||||
"""
|
||||
:param formatter_name: for "-f" argument
|
||||
:param feature_names: feature names or folders behave arguments
|
||||
:param scenario_n_options: list of ["-n", "scenario_name"]
|
||||
|
||||
|
||||
:return: True if launched as django-behave. Otherwise false and need to be launched as plain behave
|
||||
"""
|
||||
if "DJANGO_SETTINGS_MODULE" not in os.environ:
|
||||
return False
|
||||
try:
|
||||
import django
|
||||
from django.core.management import ManagementUtility
|
||||
|
||||
from behave_django import __version__ # To make sure version exists
|
||||
django.setup()
|
||||
from django.apps import apps
|
||||
|
||||
if apps.is_installed("behave_django"):
|
||||
base = sys.argv[0]
|
||||
rest = sys.argv[1:]
|
||||
sys.argv = [base] + rest + ["behave", "-f{0}".format(formatter_name)] + feature_names + scenario_n_options
|
||||
print("manage.py " + " ".join(sys.argv[1:]))
|
||||
ManagementUtility().execute()
|
||||
return True
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -8,27 +8,21 @@ Other args are tag expressionsin format (--tags=.. --tags=..).
|
||||
See https://pythonhosted.org/behave/behave.html#tag-expression
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# TODO: Rewrite using Formatters API (http://behave.readthedocs.io/en/latest/formatters.html) before any change
|
||||
#
|
||||
|
||||
|
||||
import functools
|
||||
import glob
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from behave import __version__ as behave_version
|
||||
from behave.formatter.base import Formatter
|
||||
from behave.model import Step, ScenarioOutline, Feature, Scenario
|
||||
from behave.tag_expression import TagExpression
|
||||
import re
|
||||
|
||||
import _bdd_utils
|
||||
from distutils import version
|
||||
from behave import __version__ as behave_version
|
||||
from _jb_django_behave import run_as_django_behave
|
||||
import _bdd_utils
|
||||
import tcmessages
|
||||
from _jb_utils import VersionAgnosticUtils
|
||||
|
||||
_MAX_STEPS_SEARCH_FEATURES = 5000 # Do not look for features in folder that has more that this number of children
|
||||
_FEATURES_FOLDER = 'features' # "features" folder name.
|
||||
|
||||
@@ -36,6 +30,8 @@ __author__ = 'Ilya.Kazakevich'
|
||||
|
||||
from behave import configuration, runner
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def _get_dirs_to_run(base_dir_to_search):
|
||||
"""
|
||||
@@ -81,7 +77,7 @@ class _RunnerWrapper(runner.Runner):
|
||||
"""
|
||||
:type config configuration.Configuration
|
||||
:param config behave configuration
|
||||
:type hooks dict
|
||||
:type hooks dict or empty if new runner mode
|
||||
:param hooks hooks in format "before_scenario" => f(context, scenario) to load after/before hooks, provided by user
|
||||
"""
|
||||
super(_RunnerWrapper, self).__init__(config)
|
||||
@@ -132,7 +128,6 @@ class _BehaveRunner(_bdd_utils.BddRunner):
|
||||
BddRunner for behave
|
||||
"""
|
||||
|
||||
|
||||
def __process_hook(self, is_started, context, element):
|
||||
"""
|
||||
Hook to be installed. Reports steps, features etc.
|
||||
@@ -201,7 +196,7 @@ class _BehaveRunner(_bdd_utils.BddRunner):
|
||||
def _collect_trace(self, element, utils):
|
||||
return u"".join([utils.to_unicode(l) for l in traceback.format_tb(element.exc_traceback)])
|
||||
|
||||
def __init__(self, config, base_dir):
|
||||
def __init__(self, config, base_dir, use_old_runner):
|
||||
"""
|
||||
:type config configuration.Configuration
|
||||
"""
|
||||
@@ -215,12 +210,11 @@ class _BehaveRunner(_bdd_utils.BddRunner):
|
||||
"after_scenario": functools.partial(self.__process_hook, False),
|
||||
"before_step": functools.partial(self.__process_hook, True),
|
||||
"after_step": functools.partial(self.__process_hook, False)
|
||||
})
|
||||
} if use_old_runner else dict())
|
||||
|
||||
def _run_tests(self):
|
||||
self.__real_runner.run()
|
||||
|
||||
|
||||
def __filter_scenarios_by_args(self, scenario):
|
||||
"""
|
||||
Filters out scenarios that should be skipped by tags or scenario names
|
||||
@@ -237,7 +231,6 @@ class _BehaveRunner(_bdd_utils.BddRunner):
|
||||
return True # No tags nor names are required
|
||||
return isinstance(expected_tags, TagExpression) and expected_tags.check(scenario.tags)
|
||||
|
||||
|
||||
def _get_features_to_run(self):
|
||||
self.__real_runner.dry_run = True
|
||||
self.__real_runner.run()
|
||||
@@ -264,18 +257,11 @@ class _BehaveRunner(_bdd_utils.BddRunner):
|
||||
|
||||
if __name__ == "__main__":
|
||||
# TODO: support all other params instead
|
||||
|
||||
class _Null(Formatter):
|
||||
"""
|
||||
Null formater to prevent stdout output
|
||||
"""
|
||||
pass
|
||||
|
||||
command_args = list(filter(None, sys.argv[1:]))
|
||||
if command_args:
|
||||
if "--junit" in command_args:
|
||||
raise Exception("--junit report type for Behave is unsupported in PyCharm. \n "
|
||||
"See: https://youtrack.jetbrains.com/issue/PY-14219")
|
||||
"See: https://youtrack.jetbrains.com/issue/PY-14219")
|
||||
_bdd_utils.fix_win_drive(command_args[0])
|
||||
(base_dir, scenario_names, what_to_run) = _bdd_utils.get_what_to_run_by_env(os.environ)
|
||||
|
||||
@@ -284,16 +270,45 @@ if __name__ == "__main__":
|
||||
|
||||
my_config = configuration.Configuration(command_args=command_args)
|
||||
|
||||
# Temporary workaround to support API changes in 1.2.5
|
||||
if version.LooseVersion(behave_version) >= version.LooseVersion("1.2.5"):
|
||||
from behave.formatter import _registry
|
||||
_registry.register_as("com.intellij.python.null",_Null)
|
||||
loose_version = version.LooseVersion(behave_version)
|
||||
assert loose_version >= version.LooseVersion("1.2.5"), "Version not supported, please upgrade Behave"
|
||||
|
||||
# New version supports 1.2.6 only
|
||||
use_old_runner = "PYCHARM_BEHAVE_OLD_RUNNER" in os.environ or loose_version < version.LooseVersion("1.2.6")
|
||||
from behave.formatter import _registry
|
||||
|
||||
FORMAT_NAME = "com.jetbrains.pycharm.formatter"
|
||||
if use_old_runner:
|
||||
class _Null(Formatter):
|
||||
"""
|
||||
Null formater to prevent stdout output
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
_registry.register_as(FORMAT_NAME, _Null)
|
||||
else:
|
||||
from behave.formatter import formatters
|
||||
formatters.register_as(_Null, "com.intellij.python.null")
|
||||
custom_messages = tcmessages.TeamcityServiceMessages()
|
||||
# Not safe to import it in old mode
|
||||
from teamcity.jb_behave_formatter import TeamcityFormatter
|
||||
|
||||
|
||||
my_config.format = ["com.intellij.python.null"] # To prevent output to stdout
|
||||
class TeamcityFormatterWithLocation(TeamcityFormatter):
|
||||
|
||||
def _report_suite_started(self, suite, suite_name):
|
||||
location = suite.location
|
||||
custom_messages.testSuiteStarted(suite_name,
|
||||
_bdd_utils.get_location(base_dir, location.filename, location.line))
|
||||
|
||||
def _report_test_started(self, test, test_name):
|
||||
location = test.location
|
||||
custom_messages.testStarted(test_name,
|
||||
_bdd_utils.get_location(base_dir, location.filename, location.line))
|
||||
|
||||
|
||||
_registry.register_as(FORMAT_NAME, TeamcityFormatterWithLocation)
|
||||
|
||||
my_config.format = [FORMAT_NAME] # To prevent output to stdout
|
||||
my_config.reporters = [] # To prevent summary to stdout
|
||||
my_config.stdout_capture = False # For test output
|
||||
my_config.stderr_capture = False # For test output
|
||||
@@ -307,4 +322,7 @@ if __name__ == "__main__":
|
||||
my_config.paths = list(features)
|
||||
if what_to_run and not my_config.paths:
|
||||
raise Exception("Nothing to run in {0}".format(what_to_run))
|
||||
_BehaveRunner(my_config, base_dir).run()
|
||||
|
||||
# Run as Django if supported, run plain otherwise
|
||||
if not run_as_django_behave(FORMAT_NAME, what_to_run, command_args):
|
||||
_BehaveRunner(my_config, base_dir, use_old_runner).run()
|
||||
|
||||
Reference in New Issue
Block a user