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 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 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_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 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 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"*": 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_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): # -- SIMPLISTIC 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 feature(self, feature): if feature.status == 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) now = datetime.now() 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))) # -- SINCE: Pyautomators-1.2.6.dev0 if self.show_timestamp: suite.set(u'timestamp', _text(now.isoformat())) if self.show_hostname: suite.set(u'hostname', _text(gethostname())) 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'TEST-%s.xml' % feature_filename report_filename = os.path.join(report_dirname, report_basename) tree.write(codecs.open(report_filename, "wb"), "UTF-8")
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 `Pyautomators.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 Pyautomators.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_Pyautomators(config, runner_class=None): """Run Pyautomators with configuration. :param config: Configuration object for Pyautomators. :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("Pyautomators " + __version__) return 0 if config.tags_help: print(TAG_HELP) return 0 if config.lang_list: from Pyautomators.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 Pyautomators.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 Pyautomators 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 _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) ##retirado para analise da necessidade de verificar as saidas #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)