blob: 6aaa566df719f6c8cd1e731ab0f4f8b673e0eecc [file] [log] [blame]
# coding=utf-8
"""
BDD lettuce framework runner
"""
__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):
"""
TODO: Runs lettuce
"""
def __init__(self, base_dir):
"""
:param base_dir base directory to run tests in
:type base_dir: str
"""
self.base_dir = base_dir
self.runner = lettuce.Runner(base_dir)
self.messages = tcmessages.TeamcityServiceMessages()
self.test_start_time = None
def report_tests(self):
"""
:returns : number of tests
:rtype : int
"""
result = 0
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)
def report_scenario_started(self, 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()
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()