def __str__(self): arg0 = _text(self.args[0]) if self.filename: filename = _text(self.filename) return u'Failed to parse "%s": %s' % (filename, arg0) else: return u'Failed to parse <string>: %s' % arg0
def __str__(self): arg0 = _text(self.args[0]) if self.filename: filename = _text(self.filename, sys.getfilesystemencoding()) return u'Failed to parse "%s": %s' % (filename, arg0) else: return u"Failed to parse <string>: %s" % arg0
def captured(self): """Provides access of the captured output data. :return: Object that stores the captured output parts (as Captured). """ stdout = None stderr = None log_out = None if self.config.stdout_capture and self.stdout_capture: stdout = _text(self.stdout_capture.getvalue()) if self.config.stderr_capture and self.stderr_capture: stderr = _text(self.stderr_capture.getvalue()) if self.config.log_capture and self.log_capture: log_out = _text(self.log_capture.getvalue()) return Captured(stdout, stderr, log_out)
def make_report(self): """Makes a detailled report of the captured output data. :returns: Report as string. """ report_parts = [] if self.stdout: parts = ["Captured stdout:", _text(self.stdout).rstrip(), ""] report_parts.extend(parts) if self.stderr: parts = ["Captured stderr:", _text(self.stderr).rstrip(), ""] report_parts.extend(parts) if self.log_output: parts = ["Captured logging:", _text(self.log_output)] report_parts.extend(parts) return self.linesep.join(report_parts).strip()
def __str__(self): filename = self.filename if isinstance(filename, six.binary_type): filename = _text(filename, "utf-8") if self.line is None: return filename return u"%s:%d" % (filename, self.line)
def doc_string(self, doc_string): #self.stream.write(' """' + doc_string.content_type + '\n') doc_string = _text(doc_string) prefix = u' ' self.stream.write(u'%s"""\n' % prefix) doc_string = escape_triple_quotes(indent(doc_string, prefix)) self.stream.write(doc_string) self.stream.write(u'\n%s"""\n' % prefix) self.stream.flush()
def test_issue(exception_class, message): with pytest.raises(exception_class) as e: # runner.run() raise_exception(exception_class, message) # -- SHOULD NOT RAISE EXCEPTION HERE: text = _text(e) # -- DIAGNOSTICS: print(u"text"+ text) print(u"exception: %s" % e)
def make_feature_filename(self, feature): filename = None for path in self.config.paths: if feature.filename.startswith(path): filename = feature.filename[len(path) + 1:] break if not filename: # -- NOTE: Directory path (subdirs) are taken into account. filename = feature.location.relpath(self.config.base_dir) filename = filename.rsplit('.', 1)[0] filename = filename.replace('\\', '/').replace('/', '.') return _text(filename)
def feature(self, feature): if feature.status == "skipped" and not self.show_skipped: # -- SKIP-OUTPUT: If skipped features should not be shown. return feature_filename = self.make_feature_filename(feature) classname = feature_filename report = FeatureReportData(feature, feature_filename) suite = ElementTree.Element(u'testsuite') feature_name = feature.name or feature_filename suite.set(u'name', u'%s.%s' % (classname, feature_name)) # -- BUILD-TESTCASES: From scenarios for scenario in feature: if isinstance(scenario, ScenarioOutline): scenario_outline = scenario self._process_scenario_outline(scenario_outline, report) else: self._process_scenario(scenario, report) # -- ADD TESTCASES to testsuite: for testcase in report.testcases: suite.append(testcase) suite.set(u'tests', _text(report.counts_tests)) suite.set(u'errors', _text(report.counts_errors)) suite.set(u'failures', _text(report.counts_failed)) suite.set(u'skipped', _text(report.counts_skipped)) # WAS: skips suite.set(u'time', _text(round(feature.duration, 6))) if not os.path.exists(self.config.junit_directory): # -- ENSURE: Create multiple directory levels at once. os.makedirs(self.config.junit_directory) tree = ElementTreeWithCDATA(suite) report_dirname = self.config.junit_directory report_basename = u'TESTS-%s.xml' % feature_filename report_filename = os.path.join(report_dirname, report_basename) tree.write(codecs.open(report_filename, "wb"), "UTF-8")
def report_used_step_definitions(self): # -- STEP: Used step definitions. # ORDERING: Sort step definitions by file location. get_location = lambda x: x[0].location step_definition_items = self.step_usage_database.items() step_definition_items = sorted(step_definition_items, key=get_location) for step_definition, steps in step_definition_items: stepdef_text = self.describe_step_definition(step_definition) steps_text = [u" %s %s" % (step.keyword, step.name) for step in steps] steps_text.append(stepdef_text) max_size = compute_words_maxsize(steps_text) if max_size < self.min_location_column: max_size = self.min_location_column schema = u"%-" + _text(max_size) + "s # %s\n" self.stream.write(schema % (stepdef_text, step_definition.location)) schema = u"%-" + _text(max_size) + "s # %s\n" for step, step_text in zip(steps, steps_text): self.stream.write(schema % (step_text, step.location)) self.stream.write("\n")
def describe_step(self, step): status_text = _text(step.status.name) if self.show_timings: status_text += u" in %0.3fs" % step.duration text = u'%s %s ... ' % (step.keyword, step.name) text += u'%s\n' % status_text if self.show_multiline: prefix = make_indentation(2) if step.text: text += ModelDescriptor.describe_docstring(step.text, prefix) elif step.table: text += ModelDescriptor.describe_table(step.table, prefix) return text
def report_steps_by_type(self): """Show an overview of the existing step implementations per step type. """ # pylint: disable=too-many-branches assert set(self.step_types) == set(self.step_registry.steps.keys()) language = self.config.lang or "en" language_keywords = i18n.languages[language] for step_type in self.step_types: steps = list(self.step_registry.steps[step_type]) if step_type != "step": steps.extend(self.step_registry.steps["step"]) if not steps: continue # -- PREPARE REPORT: For a step-type. step_type_name = step_type.upper() if step_type == "step": step_keyword = "* " step_type_name = "GENERIC" else: # step_keyword = step_type.capitalize() keywords = language_keywords[step_type] if keywords[0] == u"* " or keywords[0] == u"*": # -- CASE: Skip over generic-step keyword and # use next keyword (as default) for this step_type. assert len(keywords) > 1 step_keyword = keywords[1] else: step_keyword = keywords[0] steps_text = [u"%s%s" % (step_keyword, step.pattern) for step in steps] if self.shows_location: max_size = compute_words_maxsize(steps_text) if max_size < self.min_location_column: max_size = self.min_location_column schema = u" %-" + _text(max_size) + "s # %s\n" else: schema = u" %s\n" # -- REPORT: message = "%s STEP DEFINITIONS[%s]:\n" self.stream.write(message % (step_type_name, len(steps))) for step, step_text in zip(steps, steps_text): if self.shows_location: self.stream.write(schema % (step_text, step.location)) else: self.stream.write(schema % step_text) self.stream.write("\n")
def report_tag_counts(self): # -- PREPARE REPORT: ordered_tags = sorted(list(self.tag_counts.keys())) tag_maxsize = compute_words_maxsize(ordered_tags) schema = " @%-" + _text(tag_maxsize) + "s %4d (used for %s)\n" # -- EMIT REPORT: self.stream.write("TAG COUNTS (alphabetically sorted):\n") for tag in ordered_tags: tag_data = self.tag_counts[tag] counts = len(tag_data) details = self.get_tag_count_details(tag_data) self.stream.write(schema % (tag, counts, details)) self.stream.write("\n")
def feature(self, feature): feature_filename = self.make_feature_filename(feature) classname = feature_filename report = FeatureReportData(feature, feature_filename) suite = ElementTree.Element(u"testsuite") feature_name = feature.name or feature_filename suite.set(u"name", u"%s.%s" % (classname, feature_name)) # -- BUILD-TESTCASES: From scenarios for scenario in feature: if isinstance(scenario, ScenarioOutline): scenario_outline = scenario self._process_scenario_outline(scenario_outline, report) else: self._process_scenario(scenario, report) # -- ADD TESTCASES to testsuite: for testcase in report.testcases: suite.append(testcase) suite.set(u"tests", _text(report.counts_tests)) suite.set(u"errors", _text(report.counts_errors)) suite.set(u"failures", _text(report.counts_failed)) suite.set(u"skipped", _text(report.counts_skipped)) # WAS: skips suite.set(u"time", _text(round(feature.duration, 6))) if not os.path.exists(self.config.junit_directory): # -- ENSURE: Create multiple directory levels at once. os.makedirs(self.config.junit_directory) tree = ElementTreeWithCDATA(suite) report_dirname = self.config.junit_directory report_basename = u"TESTS-%s.xml" % feature_filename report_filename = os.path.join(report_dirname, report_basename) tree.write(codecs.open(report_filename, "wb"), "UTF-8")
def report_tag_counts_by_usage(self): # -- PREPARE REPORT: compare_tag_counts_size = lambda x: len(self.tag_counts[x]) ordered_tags = sorted(list(self.tag_counts.keys()), key=compare_tag_counts_size) tag_maxsize = compute_words_maxsize(ordered_tags) schema = " @%-" + _text(tag_maxsize) + "s %4d (used for %s)\n" # -- EMIT REPORT: self.stream.write("TAG COUNTS (most often used first):\n") for tag in ordered_tags: tag_data = self.tag_counts[tag] counts = len(tag_data) details = self.get_tag_count_details(tag_data) self.stream.write(schema % (tag, counts, details)) self.stream.write("\n")
def report_tags_by_locations(self): # -- PREPARE REPORT: locations = set() for tag_elements in self.tag_counts.values(): locations.update([six.text_type(x.location) for x in tag_elements]) location_column_size = compute_words_maxsize(locations) schema = u" %-" + _text(location_column_size) + "s %s\n" # -- EMIT REPORT: self.stream.write("TAG LOCATIONS (alphabetically ordered):\n") for tag in sorted(self.tag_counts): self.stream.write(" @%s:\n" % tag) for element in self.tag_counts[tag]: info = u"%s: %s" % (element.keyword, element.name) self.stream.write(schema % (element.location, info)) self.stream.write("\n") self.stream.write("\n")
def add_step_definition(self, keyword, step_text, func): step_location = Match.make_location(func) step_type = keyword.lower() step_text = _text(step_text) step_definitions = self.steps[step_type] for existing in step_definitions: if self.same_step_definition(existing, step_text, step_location): # -- EXACT-STEP: Same step function is already registered. # This may occur when a step module imports another one. return elif existing.match(step_text): message = u"%s has already been defined in\n existing step %s" new_step = u"@%s('%s')" % (step_type, step_text) existing.step_type = step_type existing_step = existing.describe() existing_step += u" at %s" % existing.location raise AmbiguousStep(message % (new_step, existing_step)) step_definitions.append(get_matcher(func, step_text))
def report_undefined_steps(self): if not self.undefined_steps: return # -- STEP: Undefined steps. undefined_steps = sorted(self.undefined_steps, key=attrgetter("location")) steps_text = [u" %s %s" % (step.keyword, step.name) for step in undefined_steps] max_size = compute_words_maxsize(steps_text) if max_size < self.min_location_column: max_size = self.min_location_column self.stream.write("\nUNDEFINED STEPS[%d]:\n" % len(steps_text)) schema = u"%-" + _text(max_size) + "s # %s\n" for step, step_text in zip(undefined_steps, steps_text): self.stream.write(schema % (step_text, step.location))
def write_table(self, table): """ Write a ReST simple table. EXAMPLE: ========================================= ===== ===== ===== ===== Step Definition Given When Then Step ========================================= ===== ===== ===== ===== Given a file named "{filename}" contains Then the file "{filename}" should ... ========================================= ===== ===== ===== ===== :param table: Table to render (as `behave.model.Table`) .. todo:: Column alignments """ assert self.stream # -- STEP: Determine table layout dimensions cols_size = [] separator_parts = [] row_schema_parts = [] for col_index, heading in enumerate(table.headings): column = [six.text_type(row[col_index]) for row in table.rows] column.append(heading) column_size = compute_words_maxsize(column) cols_size.append(column_size) separator_parts.append("=" * column_size) row_schema_parts.append("%-" + _text(column_size) + "s") separator = " ".join(separator_parts) + "\n" row_schema = " ".join(row_schema_parts) + "\n" self.stream.write("\n") # -- ENSURE: Empty line before table start. self.stream.write(separator) self.stream.write(row_schema % tuple(table.headings)) self.stream.write(separator) for row in table.rows: self.stream.write(row_schema % tuple(row)) self.stream.write(separator) self.stream.write("\n") # -- ENSURE: Empty line after table end.
def print_formatters(title=None, stream=None): """Prints the list of available formatters and their description. :param title: Optional title (as string). :param stream: Optional, output stream to use (default: sys.stdout). """ from behave.formatter._registry import format_items from operator import itemgetter if stream is None: stream = sys.stdout if title: stream.write(u"%s\n" % title) format_items = sorted(format_items(resolved=True), key=itemgetter(0)) format_names = [item[0] for item in format_items] column_size = compute_words_maxsize(format_names) schema = u" %-"+ _text(column_size) +"s %s\n" for name, formatter_class in format_items: formatter_description = getattr(formatter_class, "description", "") stream.write(schema % (name, formatter_description))
def report_unused_step_definitions(self): unused_step_definitions = self.select_unused_step_definitions() if not unused_step_definitions: return # -- STEP: Prepare report for unused step definitions. # ORDERING: Sort step definitions by file location. get_location = lambda x: x.location step_definitions = sorted(unused_step_definitions, key=get_location) step_texts = [self.describe_step_definition(step_definition) for step_definition in step_definitions] max_size = compute_words_maxsize(step_texts) if max_size < self.min_location_column-2: max_size = self.min_location_column-2 # -- STEP: Write report. schema = u" %-" + _text(max_size) + "s # %s\n" self.stream.write("UNUSED STEP DEFINITIONS[%d]:\n" % len(step_texts)) for step_definition, step_text in zip(step_definitions, step_texts): self.stream.write(schema % (step_text, step_definition.location))
def report_failures(self): if self.failures: separator = "-" * 80 self.stream.write(u"%s\n" % separator) unicode_errors = 0 for step in self.failures: try: self.stream.write(u"FAILURE in step '%s' (%s):\n" % \ (step.name, step.location)) self.stream.write(u"%s\n" % step.error_message) self.stream.write(u"%s\n" % separator) except UnicodeError as e: self.stream.write(u"%s while reporting failure in %s\n" % \ (e.__class__.__name__, step.location)) self.stream.write(u"ERROR: %s\n" % \ _text(e, encoding=self.stream.encoding)) unicode_errors += 1 if unicode_errors: msg = u"HINT: %d unicode errors occured during failure reporting.\n" self.stream.write(msg % unicode_errors) self.stream.flush()
def run_behave(config, runner_class=None): """Run behave with configuration (and optional runner class). :param config: Configuration object for behave. :param runner_class: Runner class to use or none (use Runner class). :return: 0, if successful. Non-zero on failure. .. note:: BEST EFFORT, not intended for multi-threaded usage. """ # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements if runner_class is None: runner_class = Runner if config.version: print("behave " + BEHAVE_VERSION) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: print_language_list() return 0 if config.lang_help: print_language_help(config) return 0 if not config.format: config.format = [config.default_format] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if "help" in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print("CONFIG-ERROR: More outfiles (%d) than formatters (%d)." % \ (len(config.outputs), len(config.format))) return 1 # -- MAIN PART: failed = True try: reset_runtime() runner = runner_class(config) failed = runner.run() except ParserError as e: print(u"ParserError: %s" % e) except ConfigError as e: print(u"ConfigError: %s" % e) except FileNotFoundError as e: print(u"FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print(u"InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print(u"InvalidFilenameError: %s" % e) except ConstraintError as e: print(u"ConstraintError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print(u"Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code
def _process_scenario(self, scenario, report): """ Process a scenario and append information to JUnit report object. This corresponds to a JUnit testcase: * testcase.@classname = f(filename) +'.'+ feature.name * testcase.@name = scenario.name * testcase.@status = scenario.status * testcase.@time = scenario.duration Distinguishes now between failures and errors. Failures are AssertationErrors: expectation is violated/not met. Errors are unexpected RuntimeErrors (all other exceptions). If a failure/error occurs, the step, that caused the failure, and its location are provided now. :param scenario: Scenario to process. :param report: Context object to store/add info to (outgoing param). """ # pylint: disable=too-many-locals, too-many-branches, too-many-statements assert isinstance(scenario, Scenario) assert not isinstance(scenario, ScenarioOutline) if scenario.status != "skipped" or self.show_skipped: # -- NOTE: Count only if not-skipped or skipped should be shown. report.counts_tests += 1 classname = report.classname feature = report.feature feature_name = feature.name if not feature_name: feature_name = self.make_feature_filename(feature) case = ElementTree.Element('testcase') case.set(u'classname', u'%s.%s' % (classname, feature_name)) case.set(u'name', scenario.name or '') case.set(u'status', scenario.status) case.set(u'time', _text(round(scenario.duration, 6))) step = None if scenario.status == 'failed': for status in ('failed', 'undefined'): step = self.select_step_with_status(status, scenario) if step: break assert step, "OOPS: No failed step found in scenario: %s" % scenario.name assert step.status in ('failed', 'undefined') element_name = 'failure' if isinstance(step.exception, (AssertionError, type(None))): # -- FAILURE: AssertionError report.counts_failed += 1 else: # -- UNEXPECTED RUNTIME-ERROR: report.counts_errors += 1 element_name = 'error' # -- COMMON-PART: failure = ElementTree.Element(element_name) step_text = self.describe_step(step).rstrip() text = u"\nFailing step: %s\nLocation: %s\n" % (step_text, step.location) message = _text(step.exception) if len(message) > 80: message = message[:80] + "..." failure.set(u'type', step.exception.__class__.__name__) failure.set(u'message', message) text += _text(step.error_message) failure.append(CDATA(text)) case.append(failure) elif scenario.status in ("skipped", "untested") and self.show_skipped: report.counts_skipped += 1 step = self.select_step_with_status('undefined', scenario) if step: # -- UNDEFINED-STEP: report.counts_failed += 1 failure = ElementTree.Element(u'failure') failure.set(u'type', u'undefined') failure.set(u'message', (u'Undefined Step: %s' % step.name)) case.append(failure) else: skip = ElementTree.Element(u'skipped') case.append(skip) # Create stdout section for each test case stdout = ElementTree.Element(u'system-out') text = self.describe_scenario(scenario) # Append the captured standard output if scenario.stdout: output = _text(scenario.stdout) text += u'\nCaptured stdout:\n%s\n' % output stdout.append(CDATA(text)) case.append(stdout) # Create stderr section for each test case if scenario.stderr: stderr = ElementTree.Element(u'system-err') output = _text(scenario.stderr) text = u'\nCaptured stderr:\n%s\n' % output stderr.append(CDATA(text)) case.append(stderr) if scenario.status != "skipped" or self.show_skipped: report.testcases.append(case)
def _process_scenario(self, scenario, report): """ Process a scenario and append information to JUnit report object. This corresponds to a JUnit testcase: * testcase.@classname = f(filename) +'.'+ feature.name * testcase.@name = scenario.name * testcase.@status = scenario.status * testcase.@time = scenario.duration Distinguishes now between failures and errors. Failures are AssertationErrors: expectation is violated/not met. Errors are unexpected RuntimeErrors (all other exceptions). If a failure/error occurs, the step, that caused the failure, and its location are provided now. :param scenario: Scenario to process. :param report: Context object to store/add info to (outgoing param). """ assert isinstance(scenario, Scenario) assert not isinstance(scenario, ScenarioOutline) report.counts_tests += 1 classname = report.classname feature = report.feature feature_name = feature.name if not feature_name: feature_name = self.make_feature_filename(feature) case = ElementTree.Element('testcase') case.set(u'classname', u'%s.%s' % (classname, feature_name)) case.set(u'name', scenario.name or '') case.set(u'status', scenario.status) case.set(u'time', _text(round(scenario.duration, 6))) step = None if scenario.status == 'failed': for status in ('failed', 'undefined'): step = self.select_step_with_status(status, scenario) if step: break assert step, "OOPS: No failed step found in scenario: %s" % scenario.name assert step.status in ('failed', 'undefined') element_name = 'failure' if isinstance(step.exception, (AssertionError, type(None))): # -- FAILURE: AssertionError report.counts_failed += 1 else: # -- UNEXPECTED RUNTIME-ERROR: report.counts_errors += 1 element_name = 'error' # -- COMMON-PART: failure = ElementTree.Element(element_name) step_text = self.describe_step(step).rstrip() text = u"\nFailing step: %s\nLocation: %s\n" % (step_text, step.location) message = _text(step.exception) if len(message) > 80: message = message[:80] + "..." failure.set(u'type', step.exception.__class__.__name__) failure.set(u'message', message) text += _text(step.error_message) failure.append(CDATA(text)) case.append(failure) elif scenario.status in ('skipped', 'untested'): report.counts_skipped += 1 step = self.select_step_with_status('undefined', scenario) if step: # -- UNDEFINED-STEP: report.counts_failed += 1 failure = ElementTree.Element(u'failure') failure.set(u'type', u'undefined') failure.set(u'message', (u'Undefined Step: %s' % step.name)) case.append(failure) else: skip = ElementTree.Element(u'skipped') case.append(skip) # Create stdout section for each test case stdout = ElementTree.Element(u'system-out') text = self.describe_scenario(scenario) # Append the captured standard output if scenario.stdout: output = _text(scenario.stdout) text += u'\nCaptured stdout:\n%s\n' % output stdout.append(CDATA(text)) case.append(stdout) # Create stderr section for each test case if scenario.stderr: stderr = ElementTree.Element(u'system-err') output = _text(scenario.stderr) text = u'\nCaptured stderr:\n%s\n' % output stderr.append(CDATA(text)) case.append(stderr) report.testcases.append(case)
def _process_scenario(self, scenario, report): """Process a scenario and append information to JUnit report object. This corresponds to a JUnit testcase: * testcase.@classname = f(filename) +'.'+ feature.name * testcase.@name = scenario.name * testcase.@status = scenario.status * testcase.@time = scenario.duration Distinguishes now between failures and errors. Failures are AssertationErrors: expectation is violated/not met. Errors are unexpected RuntimeErrors (all other exceptions). If a failure/error occurs, the step, that caused the failure, and its location are provided now. :param scenario: Scenario to process. :param report: Context object to store/add info to (outgoing param). """ # pylint: disable=too-many-locals, too-many-branches, too-many-statements assert isinstance(scenario, Scenario) assert not isinstance(scenario, ScenarioOutline) if scenario.status != Status.skipped or self.show_skipped: # -- NOTE: Count only if not-skipped or skipped should be shown. report.counts_tests += 1 classname = report.classname feature = report.feature feature_name = feature.name if not feature_name: feature_name = self.make_feature_filename(feature) case = ElementTree.Element('testcase') case.set(u"classname", u"%s.%s" % (classname, feature_name)) case.set(u"name", scenario.name or "") case.set(u"status", scenario.status.name) case.set(u"time", _text(round(scenario.duration, 6))) case.set(u"tags", _text(self.describe_tags(scenario.tags))) step = None failing_step = None if scenario.status == Status.failed: for status in (Status.failed, Status.undefined): step = self.select_step_with_status(status, scenario) if step: break # -- NOTE: Scenario may fail now due to hook-errors. element_name = "failure" if step and isinstance(step.exception, (AssertionError, type(None))): # -- FAILURE: AssertionError assert step.status in (Status.failed, Status.undefined) report.counts_failed += 1 else: # -- UNEXPECTED RUNTIME-ERROR: report.counts_errors += 1 element_name = "error" # -- COMMON-PART: failure = ElementTree.Element(element_name) if step: step_text = self.describe_step(step).rstrip() text = u"\nFailing step: %s\nLocation: %s\n" % \ (step_text, step.location) message = _text(step.exception) failure.set(u'type', step.exception.__class__.__name__) failure.set(u'message', message) text += _text(step.error_message) else: # -- MAYBE: Hook failure before any step is executed. failure_type = "UnknownError" if scenario.exception: failure_type = scenario.exception.__class__.__name__ failure.set(u'type', failure_type) failure.set(u'message', scenario.error_message or "") traceback_lines = traceback.format_tb(scenario.exc_traceback) traceback_lines.insert(0, u"Traceback:\n") text = _text(u"".join(traceback_lines)) failure.append(CDATA(text)) case.append(failure) elif (scenario.status in (Status.skipped, Status.untested) and self.show_skipped): report.counts_skipped += 1 step = self.select_step_with_status(Status.undefined, scenario) if step: # -- UNDEFINED-STEP: report.counts_failed += 1 failure = ElementTree.Element(u"failure") failure.set(u"type", u"undefined") failure.set(u"message", (u"Undefined Step: %s" % step.name)) case.append(failure) else: skip = ElementTree.Element(u'skipped') case.append(skip) if scenario.status_error == StatusError.none: case.set(u"status_error", "none")
def add_step_definition(self, keyword, step_text, func, matcher=None): step_type = keyword.lower() step_text = _text(step_text) self.steps[step_type].append(self.get_matcher(func, step_text, matcher))
if scenario.status_error == StatusError.none: case.set(u"status_error", "none") elif scenario.status_error == StatusError.timeout: case.set(u"status_error", "timeout") elif scenario.status_error == StatusError.crash: case.set(u"status_error", "crash") # Create stdout section for each test case stdout = ElementTree.Element(u"system-out") text = u"" if self.show_scenarios: text = self.describe_scenario(scenario) # Append the captured standard output if scenario.captured.stdout: output = _text(scenario.captured.stdout) text += u"\nCaptured stdout:\n%s\n" % output stdout.append(CDATA(text)) case.append(stdout) # Create stderr section for each test case if scenario.captured.stderr: stderr = ElementTree.Element(u"system-err") output = _text(scenario.captured.stderr) text = u"\nCaptured stderr:\n%s\n" % output stderr.append(CDATA(text)) case.append(stderr) if scenario.status != Status.skipped or self.show_skipped: report.testcases.append(case)
def main(args=None): config = Configuration(args) if config.version: print("behave " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: from behave.i18n import languages iso_codes = languages.keys() iso_codes.sort() print("Languages available:") for iso_code in iso_codes: native = languages[iso_code]['native'][0] name = languages[iso_code]['name'][0] print(u'%s: %s / %s' % (iso_code, native, name)) return 0 if config.lang_help: from behave.i18n import languages if config.lang_help not in languages: print('%s is not a recognised language: try --lang-list' % \ config.lang_help) return 1 trans = languages[config.lang_help] print(u"Translations for %s / %s" % (trans['name'][0], trans['native'][0])) for kw in trans: if kw in 'name native'.split(): continue print(u'%16s: %s' % (kw.title().replace('_', ' '), u', '.join(w for w in trans[kw] if w != '*'))) return 0 if not config.format: config.format = [ config.default_format ] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if 'help' in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print('CONFIG-ERROR: More outfiles (%d) than formatters (%d).' % \ (len(config.outputs), len(config.format))) return 1 failed = True runner = Runner(config) try: failed = runner.run() except ParserError as e: print(u"ParseError: %s" % e) except ConfigError as e: print(u"ConfigError: %s" % e) except FileNotFoundError as e: print(u"FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print(u"InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print(u"InvalidFilenameError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print(u"Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code
def _process_scenario(self, scenario, report): """Process a scenario and append information to JUnit report object. This corresponds to a JUnit testcase: * testcase.@classname = f(filename) +'.'+ feature.name * testcase.@name = scenario.name * testcase.@status = scenario.status * testcase.@time = scenario.duration Distinguishes now between failures and errors. Failures are AssertationErrors: expectation is violated/not met. Errors are unexpected RuntimeErrors (all other exceptions). If a failure/error occurs, the step, that caused the failure, and its location are provided now. :param scenario: Scenario to process. :param report: Context object to store/add info to (outgoing param). """ # pylint: disable=too-many-locals, too-many-branches, too-many-statements assert isinstance(scenario, Scenario) assert not isinstance(scenario, ScenarioOutline) if scenario.status != Status.skipped or self.show_skipped: # -- NOTE: Count only if not-skipped or skipped should be shown. report.counts_tests += 1 classname = report.classname feature = report.feature feature_name = feature.name if not feature_name: feature_name = self.make_feature_filename(feature) case = ElementTree.Element('testcase') case.set(u"classname", u"%s.%s" % (classname, feature_name)) case.set(u"name", scenario.name or "") case.set(u"status", scenario.status.name) case.set(u"time", _text(round(scenario.duration, 6))) step = None failing_step = None if scenario.status == Status.failed: for status in (Status.failed, Status.undefined): step = self.select_step_with_status(status, scenario) if step: break # -- NOTE: Scenario may fail now due to hook-errors. element_name = "failure" if step and isinstance(step.exception, (AssertionError, type(None))): # -- FAILURE: AssertionError assert step.status in (Status.failed, Status.undefined) report.counts_failed += 1 else: # -- UNEXPECTED RUNTIME-ERROR: report.counts_errors += 1 element_name = "error" # -- COMMON-PART: failure = ElementTree.Element(element_name) if step: step_text = self.describe_step(step).rstrip() text = u"\nFailing step: %s\nLocation: %s\n" % \ (step_text, step.location) message = _text(step.exception) failure.set(u'type', step.exception.__class__.__name__) failure.set(u'message', message) text += _text(step.error_message) else: # -- MAYBE: Hook failure before any step is executed. failure_type = "UnknownError" if scenario.exception: failure_type = scenario.exception.__class__.__name__ failure.set(u'type', failure_type) failure.set(u'message', scenario.error_message or "") traceback_lines = traceback.format_tb(scenario.exc_traceback) traceback_lines.insert(0, u"Traceback:\n") text = _text(u"".join(traceback_lines)) failure.append(CDATA(text)) case.append(failure) elif (scenario.status in (Status.skipped, Status.untested) and self.show_skipped): report.counts_skipped += 1 step = self.select_step_with_status(Status.undefined, scenario) if step: # -- UNDEFINED-STEP: report.counts_failed += 1 failure = ElementTree.Element(u"failure") failure.set(u"type", u"undefined") failure.set(u"message", (u"Undefined Step: %s" % step.name)) case.append(failure) else: skip = ElementTree.Element(u'skipped') case.append(skip) # Create stdout section for each test case stdout = ElementTree.Element(u"system-out") text = u"" if self.show_scenarios: text = self.describe_scenario(scenario) # Append the captured standard output if scenario.captured.stdout: output = _text(scenario.captured.stdout) text += u"\nCaptured stdout:\n%s\n" % output stdout.append(CDATA(text)) case.append(stdout) # Create stderr section for each test case if scenario.captured.stderr: stderr = ElementTree.Element(u"system-err") output = _text(scenario.captured.stderr) text = u"\nCaptured stderr:\n%s\n" % output stderr.append(CDATA(text)) case.append(stderr) if scenario.status != Status.skipped or self.show_skipped: report.testcases.append(case)
def run(cls, command, cwd=".", **kwargs): """ Make a subprocess call, collect its output and returncode. Returns CommandResult instance as ValueObject. """ assert isinstance(command, six.string_types) command_result = CommandResult() command_result.command = command use_shell = cls.USE_SHELL if "shell" in kwargs: use_shell = kwargs.pop("shell") # -- BUILD COMMAND ARGS: if six.PY2 and isinstance(command, six.text_type): # -- PREPARE-FOR: shlex.split() # In PY2, shlex.split() requires bytes string (non-unicode). # In PY3, shlex.split() accepts unicode string. command = codecs.encode(command, "utf-8") cmdargs = shlex.split(command) # -- TRANSFORM COMMAND (optional) command0 = cmdargs[0] real_command = cls.COMMAND_MAP.get(command0, None) if real_command: cmdargs0 = real_command.split() cmdargs = cmdargs0 + cmdargs[1:] preprocessors = cls.PREPROCESSOR_MAP.get(command0) if preprocessors: cmdargs = cls.preprocess_command(preprocessors, cmdargs, command, cwd) # -- RUN COMMAND: try: process = subprocess.Popen(cmdargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=use_shell, cwd=cwd, **kwargs) out, err = process.communicate() if six.PY2: # py3: we get unicode strings, py2 not # default_encoding = "UTF-8" out = _text(out, process.stdout.encoding) err = _text(err, process.stderr.encoding) process.poll() assert process.returncode is not None command_result.stdout = out command_result.stderr = err command_result.returncode = process.returncode if cls.DEBUG: print("shell.cwd={0}".format(kwargs.get("cwd", None))) print("shell.command: {0}".format(" ".join(cmdargs))) print("shell.command.output:\n{0};".format(command_result.output)) except OSError as e: command_result.stderr = u"OSError: %s" % e command_result.returncode = e.errno assert e.errno != 0 postprocessors = cls.POSTPROCESSOR_MAP.get(command0) if postprocessors: command_result = cls.postprocess_command(postprocessors, command_result) return command_result
def run_behave(config, runner_class=None): """Run behave with configuration (and optional runner class). :param config: Configuration object for behave. :param runner_class: Runner class to use or none (use Runner class). :return: 0, if successful. Non-zero on failure. .. note:: BEST EFFORT, not intended for multi-threaded usage. """ # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements if runner_class is None: runner_class = Runner if config.version: print("behave " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: print_language_list() return 0 if config.lang_help: print_language_help(config) return 0 if not config.format: config.format = [config.default_format] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if "help" in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print("CONFIG-ERROR: More outfiles (%d) than formatters (%d)." % \ (len(config.outputs), len(config.format))) return 1 # -- MAIN PART: failed = True try: reset_runtime() runner = runner_class(config) failed = runner.run() except ParserError as e: print(u"ParserError: %s" % e) except ConfigError as e: print(u"ConfigError: %s" % e) except FileNotFoundError as e: print(u"FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print(u"InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print(u"InvalidFilenameError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print(u"Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code
def main(args=None): config = Configuration(args) if config.version: print("behave " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: from behave.i18n import languages stdout = sys.stdout if six.PY2: # -- PYTHON2: Overcome implicit encode problems (encoding=ASCII). stdout = codecs.getwriter("UTF-8")(sys.stdout) iso_codes = languages.keys() print("Languages available:") for iso_code in sorted(iso_codes): native = languages[iso_code]["native"][0] name = languages[iso_code]["name"][0] print(u'%s: %s / %s' % (iso_code, native, name), file=stdout) return 0 if config.lang_help: from behave.i18n import languages if config.lang_help not in languages: print('%s is not a recognised language: try --lang-list' % \ config.lang_help) return 1 trans = languages[config.lang_help] print(u"Translations for %s / %s" % (trans['name'][0], trans['native'][0])) for kw in trans: if kw in 'name native'.split(): continue print(u'%16s: %s' % (kw.title().replace('_', ' '), u', '.join( w for w in trans[kw] if w != '*'))) return 0 if not config.format: config.format = [config.default_format] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if 'help' in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print('CONFIG-ERROR: More outfiles (%d) than formatters (%d).' % \ (len(config.outputs), len(config.format))) return 1 failed = True runner = Runner(config) try: failed = runner.run() except ParserError as e: print(u"ParseError: %s" % e) except ConfigError as e: print(u"ConfigError: %s" % e) except FileNotFoundError as e: print(u"FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print(u"InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print(u"InvalidFilenameError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print(u"Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code
def run_behave(config, runner_class=None): """Run behave with configuration. :param config: Configuration object for behave. :param runner_class: Runner class to use or none (use Runner class). :return: 0, if successful. Non-zero on failure. .. note:: BEST EFFORT, not intended for multi-threaded usage. """ # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements if runner_class is None: runner_class = Runner if config.version: print("behave " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: from behave.i18n import languages stdout = sys.stdout if six.PY2: # -- PYTHON2: Overcome implicit encode problems (encoding=ASCII). stdout = codecs.getwriter("UTF-8")(sys.stdout) iso_codes = list(languages.keys()) print("Languages available:") for iso_code in sorted(iso_codes): native = languages[iso_code]["native"][0] name = languages[iso_code]["name"][0] print("%s: %s / %s" % (iso_code, native, name), file=stdout) return 0 if config.lang_help: from behave.i18n import languages if config.lang_help not in languages: print("%s is not a recognised language: try --lang-list" % \ config.lang_help) return 1 trans = languages[config.lang_help] print("Translations for %s / %s" % (trans["name"][0], trans["native"][0])) for kw in trans: if kw in "name native".split(): continue print("%16s: %s" % (kw.title().replace("_", " "), ", ".join( w for w in trans[kw] if w != "*"))) return 0 if not config.format: config.format = [config.default_format] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if "help" in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print("CONFIG-ERROR: More outfiles (%d) than formatters (%d)." % \ (len(config.outputs), len(config.format))) return 1 # -- MAIN PART: failed = True try: reset_runtime() runner = runner_class(config) failed = runner.run() except ParserError as e: print("ParserError: %s" % e) except ConfigError as e: print("ConfigError: %s" % e) except FileNotFoundError as e: print("FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print("InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print("InvalidFilenameError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print("Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code
def run_behave(config, runner_class=None): """Run behave with configuration. :param config: Configuration object for behave. :param runner_class: Runner class to use or none (use Runner class). :return: 0, if successful. Non-zero on failure. .. note:: BEST EFFORT, not intended for multi-threaded usage. """ # pylint: disable=too-many-branches, too-many-statements, too-many-return-statements if runner_class is None: runner_class = Runner if config.version: print("behave " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: from behave.i18n import languages stdout = sys.stdout if six.PY2: # -- PYTHON2: Overcome implicit encode problems (encoding=ASCII). stdout = codecs.getwriter("UTF-8")(sys.stdout) iso_codes = languages.keys() print("Languages available:") for iso_code in sorted(iso_codes): native = languages[iso_code]["native"][0] name = languages[iso_code]["name"][0] print(u"%s: %s / %s" % (iso_code, native, name), file=stdout) return 0 if config.lang_help: from behave.i18n import languages if config.lang_help not in languages: print("%s is not a recognised language: try --lang-list" % \ config.lang_help) return 1 trans = languages[config.lang_help] print(u"Translations for %s / %s" % (trans["name"][0], trans["native"][0])) for kw in trans: if kw in "name native".split(): continue print(u"%16s: %s" % (kw.title().replace("_", " "), u", ".join(w for w in trans[kw] if w != "*"))) return 0 if not config.format: config.format = [config.default_format] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if "help" in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print("CONFIG-ERROR: More outfiles (%d) than formatters (%d)." % \ (len(config.outputs), len(config.format))) return 1 # -- MAIN PART: failed = True try: reset_runtime() runner = runner_class(config) failed = runner.run() except ParserError as e: print(u"ParserError: %s" % e) except ConfigError as e: print(u"ConfigError: %s" % e) except FileNotFoundError as e: print(u"FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print(u"InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print(u"InvalidFilenameError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print(u"Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code
def main(args=None): config = Configuration(args) if config.version: print("behave " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: from behave.i18n import languages stdout = sys.stdout if six.PY2: # -- PYTHON2: Overcome implicit encode problems (encoding=ASCII). stdout = codecs.getwriter("UTF-8")(sys.stdout) iso_codes = languages.keys() print("Languages available:") for iso_code in sorted(iso_codes): native = languages[iso_code]["native"][0] name = languages[iso_code]["name"][0] print(u"%s: %s / %s" % (iso_code, native, name), file=stdout) return 0 if config.lang_help: from behave.i18n import languages if config.lang_help not in languages: print("%s is not a recognised language: try --lang-list" % config.lang_help) return 1 trans = languages[config.lang_help] print(u"Translations for %s / %s" % (trans["name"][0], trans["native"][0])) for kw in trans: if kw in "name native".split(): continue print(u"%16s: %s" % (kw.title().replace("_", " "), u", ".join(w for w in trans[kw] if w != "*"))) return 0 if not config.format: config.format = [config.default_format] elif config.format and "format" in config.defaults: # -- CASE: Formatter are specified in behave configuration file. # Check if formatter are provided on command-line, too. if len(config.format) == len(config.defaults["format"]): # -- NO FORMATTER on command-line: Add default formatter. config.format.append(config.default_format) if "help" in config.format: print_formatters("Available formatters:") return 0 if len(config.outputs) > len(config.format): print("CONFIG-ERROR: More outfiles (%d) than formatters (%d)." % (len(config.outputs), len(config.format))) return 1 failed = True runner = Runner(config) try: failed = runner.run() except ParserError as e: print(u"ParseError: %s" % e) except ConfigError as e: print(u"ConfigError: %s" % e) except FileNotFoundError as e: print(u"FileNotFoundError: %s" % e) except InvalidFileLocationError as e: print(u"InvalidFileLocationError: %s" % e) except InvalidFilenameError as e: print(u"InvalidFilenameError: %s" % e) except Exception as e: # -- DIAGNOSTICS: text = _text(e) print(u"Exception %s: %s" % (e.__class__.__name__, text)) raise if config.show_snippets and runner.undefined_steps: print_undefined_step_snippets(runner.undefined_steps, colored=config.color) return_code = 0 if failed: return_code = 1 return return_code