Snapshot idea/138.1503 from git://git.jetbrains.org/idea/community.git

Change-Id: Ie01af1d8710ec0ff51d90301bda1a18b0b5c0faf
diff --git a/python/helpers/pycharm/lettuce_runner.py b/python/helpers/pycharm/lettuce_runner.py
index 6aaa566..3cd1125 100644
--- a/python/helpers/pycharm/lettuce_runner.py
+++ b/python/helpers/pycharm/lettuce_runner.py
@@ -1,132 +1,112 @@
 # coding=utf-8
 """
 BDD lettuce framework runner
+TODO: Support other params (like tags) as well.
+Supports only 1 param now: folder to search "features" for.
 """
+import _bdd_utils
+
 __author__ = 'Ilya.Kazakevich'
-import os
 from lettuce.exceptions import ReasonToFail
-import time
 import sys
-import tcmessages
 import lettuce
 from lettuce import core
 
 
-# Error message about unsupported outlines
-_NO_OUTLINE_ERROR = "Outline scenarios are not supported due to https://github.com/gabrielfalcao/lettuce/issues/451"
-
-
-class LettuceRunner(object):
+class _LettuceRunner(_bdd_utils.BddRunner):
     """
-    TODO: Runs lettuce
+    Lettuce runner (BddRunner for lettuce)
     """
 
-    def __init__(self, base_dir):
+    def __init__(self, base_dir, what_to_run):
         """
+
         :param base_dir base directory to run tests in
         :type base_dir: str
+        :param what_to_run folder or file to run
+        :type what_to_run str
+        """
+        super(_LettuceRunner, self).__init__(base_dir)
+        self.__runner = lettuce.Runner(what_to_run)
 
-        """
-        self.base_dir = base_dir
-        self.runner = lettuce.Runner(base_dir)
-        self.messages = tcmessages.TeamcityServiceMessages()
-        self.test_start_time = None
+    def _get_features_to_run(self):
+        super(_LettuceRunner, self)._get_features_to_run()
+        if self.__runner.single_feature:  # We need to run one and only one feature
+            return [core.Feature.from_file(self.__runner.single_feature)]
 
-    def report_tests(self):
-        """
-        :returns : number of tests
-        :rtype : int
-        """
-        result = 0
-        for feature_file in self.runner.loader.find_feature_files():
+        # Find all features in dir
+        features = []
+        for feature_file in self.__runner.loader.find_feature_files():
             feature = core.Feature.from_file(feature_file)
-            for scenario in feature.scenarios:
-                assert isinstance(scenario, core.Scenario), scenario
-                if not scenario.outlines:
-                    result += len(scenario.steps)
-        self.messages.testCount(result)
+            assert isinstance(feature, core.Feature), feature
+            # TODO: cut out due to https://github.com/gabrielfalcao/lettuce/issues/451  Fix when this issue fixed
+            feature.scenarios = filter(lambda s: not s.outlines, feature.scenarios)
+            if feature.scenarios:
+                features.append(feature)
+        return features
 
-    def report_scenario_started(self, scenario):
+    def _run_tests(self):
+        super(_LettuceRunner, self)._run_tests()
+        self.__install_hooks()
+        self.__runner.run()
+
+    def __step(self, is_started, step):
+        """
+        Reports step start / stop
+        :type step core.Step
+        :param step: step
+        """
+        test_name = step.sentence
+        if is_started:
+            self._test_started(test_name, step.described_at)
+        elif step.passed:
+            self._test_passed(test_name)
+        elif step.failed:
+            reason = step.why
+            assert isinstance(reason, ReasonToFail), reason
+            self._test_failed(test_name, message=reason.exception, details=reason.traceback)
+        elif step.has_definition:
+            self._test_skipped(test_name, "In lettuce, we do know the reason", step.described_at)
+        else:
+            self._test_undefined(test_name, step.described_at)
+
+    def __install_hooks(self):
+        """
+        Installs required hooks
+        """
+
+        # Install hooks
+        lettuce.before.each_feature(
+            lambda f: self._feature_or_scenario(True, f.name, f.described_at))
+        lettuce.after.each_feature(
+            lambda f: self._feature_or_scenario(False, f.name, f.described_at))
+
+        lettuce.before.each_scenario(
+            lambda s: self.__scenario(True, s))
+        lettuce.after.each_scenario(
+            lambda s: self.__scenario(False, s))
+
+        lettuce.before.each_background(
+            lambda b, *args: self._background(True, b.feature.described_at))
+        lettuce.after.each_background(
+            lambda b, *args: self._background(False, b.feature.described_at))
+
+        lettuce.before.each_step(lambda s: self.__step(True, s))
+        lettuce.after.each_step(lambda s: self.__step(False, s))
+
+    def __scenario(self, is_started, scenario):
         """
         Reports scenario launched
         :type scenario core.Scenario
         :param scenario: scenario
         """
         if scenario.outlines:
-            self.messages.testIgnored(scenario.name,
-                                      _NO_OUTLINE_ERROR)
             scenario.steps = []  # Clear to prevent running. TODO: Fix when this issue fixed
             scenario.background = None  # TODO: undocumented
             return
-        self.report_suite(True, scenario.name, scenario.described_at)
-
-    def report_suite(self, is_start, name, described_at):
-        """
-        Reports some suite (scenario, feature, background etc) is started or stopped
-        :param is_start: started or not
-        :param name: suite name
-        :param described_at: where it is described (file, line)
-        :return:
-        """
-        if is_start:
-            self.messages.testSuiteStarted(name, self._gen_location(described_at))
-        else:
-            self.messages.testSuiteFinished(name)
-
-    def report_step(self, is_start, step):
-        """
-        Reports step start / stop
-        :param is_start: true if step started
-        :type step core.Step
-        :param step: step
-        """
-        test_name = step.sentence
-        if is_start:
-            self.test_start_time = time.time()
-            self.messages.testStarted(test_name, self._gen_location(step.described_at))
-        elif step.passed:
-            duration = 0
-            if self.test_start_time:
-                duration = long(time.time() - self.test_start_time)
-            self.messages.testFinished(test_name, duration=duration)
-            self.test_start_time = None
-        elif step.failed:
-            reason = step.why
-            assert isinstance(reason, ReasonToFail), reason
-            self.messages.testFailed(test_name, message=reason.exception, details=reason.traceback)
-
-    def _gen_location(self, description):
-        """
-        :param description: "described_at" (file, line)
-        :return: location in format file:line by "described_at"
-        """
-        return "file:///{}/{}:{}".format(self.base_dir, description.file, description.line)
-
-    def run(self):
-        """
-        Launches runner
-        """
-        self.report_tests()
-        self.messages.testMatrixEntered()
-
-        lettuce.before.each_feature(lambda f: self.report_suite(True, f.name, f.described_at))
-        lettuce.after.each_feature(lambda f: self.report_suite(False, f.name, f.described_at))
-
-        lettuce.before.each_scenario(lambda s: self.report_scenario_started(s))
-        lettuce.after.each_scenario(lambda s: self.report_suite(False, s.name, s.described_at))
-
-        lettuce.before.each_background(
-            lambda b, *args: self.report_suite(True, "Scenario background", b.feature.described_at))
-        lettuce.after.each_background(
-            lambda b, *args: self.report_suite(False, "Scenario background", b.feature.described_at))
-
-        lettuce.before.each_step(lambda s: self.report_step(True, s))
-        lettuce.after.each_step(lambda s: self.report_step(False, s))
-
-        self.runner.run()
+        self._feature_or_scenario(is_started, scenario.name, scenario.described_at)
 
 
 if __name__ == "__main__":
-    path = sys.argv[1] if len(sys.argv) > 1 else "."
-    assert os.path.exists(path), "{} does not exist".format(path)
-    LettuceRunner(path).run()
\ No newline at end of file
+    (base_dir, what_to_run) = _bdd_utils.get_path_by_args(sys.argv)
+    _LettuceRunner(base_dir, what_to_run).run()
\ No newline at end of file