| Tor Norbye | 2e5965e | 2014-07-25 12:24:15 -0700 | [diff] [blame^] | 1 | # coding=utf-8 |
| 2 | """ |
| 3 | BDD lettuce framework runner |
| 4 | """ |
| 5 | __author__ = 'Ilya.Kazakevich' |
| 6 | import os |
| 7 | from lettuce.exceptions import ReasonToFail |
| 8 | import time |
| 9 | import sys |
| 10 | import tcmessages |
| 11 | import lettuce |
| 12 | from lettuce import core |
| 13 | |
| 14 | |
| 15 | # Error message about unsupported outlines |
| 16 | _NO_OUTLINE_ERROR = "Outline scenarios are not supported due to https://github.com/gabrielfalcao/lettuce/issues/451" |
| 17 | |
| 18 | |
| 19 | class LettuceRunner(object): |
| 20 | """ |
| 21 | TODO: Runs lettuce |
| 22 | """ |
| 23 | |
| 24 | def __init__(self, base_dir): |
| 25 | """ |
| 26 | :param base_dir base directory to run tests in |
| 27 | :type base_dir: str |
| 28 | |
| 29 | """ |
| 30 | self.base_dir = base_dir |
| 31 | self.runner = lettuce.Runner(base_dir) |
| 32 | self.messages = tcmessages.TeamcityServiceMessages() |
| 33 | self.test_start_time = None |
| 34 | |
| 35 | def report_tests(self): |
| 36 | """ |
| 37 | :returns : number of tests |
| 38 | :rtype : int |
| 39 | """ |
| 40 | result = 0 |
| 41 | for feature_file in self.runner.loader.find_feature_files(): |
| 42 | feature = core.Feature.from_file(feature_file) |
| 43 | for scenario in feature.scenarios: |
| 44 | assert isinstance(scenario, core.Scenario), scenario |
| 45 | if not scenario.outlines: |
| 46 | result += len(scenario.steps) |
| 47 | self.messages.testCount(result) |
| 48 | |
| 49 | def report_scenario_started(self, scenario): |
| 50 | """ |
| 51 | Reports scenario launched |
| 52 | :type scenario core.Scenario |
| 53 | :param scenario: scenario |
| 54 | """ |
| 55 | if scenario.outlines: |
| 56 | self.messages.testIgnored(scenario.name, |
| 57 | _NO_OUTLINE_ERROR) |
| 58 | scenario.steps = [] # Clear to prevent running. TODO: Fix when this issue fixed |
| 59 | scenario.background = None # TODO: undocumented |
| 60 | return |
| 61 | self.report_suite(True, scenario.name, scenario.described_at) |
| 62 | |
| 63 | def report_suite(self, is_start, name, described_at): |
| 64 | """ |
| 65 | Reports some suite (scenario, feature, background etc) is started or stopped |
| 66 | :param is_start: started or not |
| 67 | :param name: suite name |
| 68 | :param described_at: where it is described (file, line) |
| 69 | :return: |
| 70 | """ |
| 71 | if is_start: |
| 72 | self.messages.testSuiteStarted(name, self._gen_location(described_at)) |
| 73 | else: |
| 74 | self.messages.testSuiteFinished(name) |
| 75 | |
| 76 | def report_step(self, is_start, step): |
| 77 | """ |
| 78 | Reports step start / stop |
| 79 | :param is_start: true if step started |
| 80 | :type step core.Step |
| 81 | :param step: step |
| 82 | """ |
| 83 | test_name = step.sentence |
| 84 | if is_start: |
| 85 | self.test_start_time = time.time() |
| 86 | self.messages.testStarted(test_name, self._gen_location(step.described_at)) |
| 87 | elif step.passed: |
| 88 | duration = 0 |
| 89 | if self.test_start_time: |
| 90 | duration = long(time.time() - self.test_start_time) |
| 91 | self.messages.testFinished(test_name, duration=duration) |
| 92 | self.test_start_time = None |
| 93 | elif step.failed: |
| 94 | reason = step.why |
| 95 | assert isinstance(reason, ReasonToFail), reason |
| 96 | self.messages.testFailed(test_name, message=reason.exception, details=reason.traceback) |
| 97 | |
| 98 | def _gen_location(self, description): |
| 99 | """ |
| 100 | :param description: "described_at" (file, line) |
| 101 | :return: location in format file:line by "described_at" |
| 102 | """ |
| 103 | return "file:///{}/{}:{}".format(self.base_dir, description.file, description.line) |
| 104 | |
| 105 | def run(self): |
| 106 | """ |
| 107 | Launches runner |
| 108 | """ |
| 109 | self.report_tests() |
| 110 | self.messages.testMatrixEntered() |
| 111 | |
| 112 | lettuce.before.each_feature(lambda f: self.report_suite(True, f.name, f.described_at)) |
| 113 | lettuce.after.each_feature(lambda f: self.report_suite(False, f.name, f.described_at)) |
| 114 | |
| 115 | lettuce.before.each_scenario(lambda s: self.report_scenario_started(s)) |
| 116 | lettuce.after.each_scenario(lambda s: self.report_suite(False, s.name, s.described_at)) |
| 117 | |
| 118 | lettuce.before.each_background( |
| 119 | lambda b, *args: self.report_suite(True, "Scenario background", b.feature.described_at)) |
| 120 | lettuce.after.each_background( |
| 121 | lambda b, *args: self.report_suite(False, "Scenario background", b.feature.described_at)) |
| 122 | |
| 123 | lettuce.before.each_step(lambda s: self.report_step(True, s)) |
| 124 | lettuce.after.each_step(lambda s: self.report_step(False, s)) |
| 125 | |
| 126 | self.runner.run() |
| 127 | |
| 128 | |
| 129 | if __name__ == "__main__": |
| 130 | path = sys.argv[1] if len(sys.argv) > 1 else "." |
| 131 | assert os.path.exists(path), "{} does not exist".format(path) |
| 132 | LettuceRunner(path).run() |