def restore(self): parser = argparse.ArgumentParser( description='Restore config settings to it\'s original values', usage="tj config restore [OPTIONS]") parser.add_argument( "-a", "--all", action="store_true", default=False, help="Will restore all config settings to its default values") CliUtils.add_standard_boolean_tj_args(parser) args = parser.parse_args(self.args[3:]) if args.all: self.config.restore() print("[{status}] Config restored to default settings!".format( status=CliUtils.format_color_string(value="OK", color="green"))) return if not self.args[3:]: print("[{status}]\tWhat do you want to restore?\n".format( status=CliUtils.format_color_string(value="ERROR", color="red"))) parser.print_help() return for option, value in args.__dict__.items(): if value is True: self.__restore_value(option, None)
def raise_import_error(error): print("[{status}] There is an import error: {error}".format( status=CliUtils.format_color_string(value="ERROR", color="red"), error=error)) print( "[{status}] 1. Make sure you have installed all of the packages required for your project " "to work".format(status=CliUtils.format_color_string( value="ERROR", color="red"))) print( "[{status}] 2. Make sure the root of your project is in the PYTHONPATH" .format(status=CliUtils.format_color_string(value="ERROR", color="red"))) raise
def __restore_value(self, option, value): try: self.config.set_value(option, value) print("[{status}]\t{option}={value}".format( status=CliUtils.format_color_string(value="OK", color="green"), option=option, value=value)) except: print( "[{status}]\tUnexpected error occurred during update of {setting}={value}" .format(status=CliUtils.format_color_string(value="ERROR", color="red"), setting=option, value=value)) CliUtils.print_color_traceback() exit(120)
def get_value(self, option, default=Undefined): section = "runtime" try: if sys.version_info[0] < 3: # Python 2 value = self.config.get(section, option, default) else: # Python 3, module is not backwards compatible and fallback has to be explicitly assigned value = self.config.get(section, option, fallback=default) return value except Exception: print( "[{status}]\tPlease check config: {path} it appears that its miss-configured." .format(status=CliUtils.format_color_string(value="ERROR", color="red"), path=CliUtils.format_color_string(value=self.path, color="red"))) raise
def __init__(self, config_name): if config_name not in [CliConstants.TJ_CONFIG_NAME]: if not os.path.exists(config_name): print( "[{status}]\tWasn't able to find config @ {path}. Please check that the file exists." .format(status=CliUtils.format_color_string(value="ERROR", color="red"), path=CliUtils.format_color_string( value=config_name, color="red"))) exit(120) self.path = config_name else: self.path = "{root}{sep}{file}".format(root=Config.get_root_dir(), file=config_name, sep=os.sep) if not os.path.exists(Config.get_root_dir()): os.makedirs(Config.get_root_dir()) if not os.path.exists(self.path): self.restore() self.config = self.__get_parser()
def update(self): parser = argparse.ArgumentParser( description= "Update configuration settings for individual properties", usage="tj config update [OPTIONS]") CliUtils.add_standard_tj_args(parser) args = parser.parse_args(self.args[3:]) if not self.args[3:]: print("[{status}]\tWhat do you want to update?\n".format( status=CliUtils.format_color_string(value="ERROR", color="red"))) parser.print_help() return for option, value in args.__dict__.items(): if value is not Undefined: self.__restore_value(option, value) print( "[{status}]\tRestore original value with tj config restore [OPTION]. tj config restore -h for more info." .format(status=CliUtils.format_color_string(value="TIP", color="blue")))
def guess_project_root(path, _module_name, error): print("[{status}] Import error: {error}".format( status=CliUtils.format_color_string(value="WARNING", color="yellow"), error=error)) possibility = "{}".format(os.sep).join(path.split(os.sep)[:-1]) print( "[{status}] Trying again with assumption that this is your project root: {assumed_root}" .format(status=CliUtils.format_color_string(value="WARNING", color="yellow"), assumed_root=CliUtils.format_color_string( value=possibility, color="yellow"))) try: sys.path.insert(0, possibility) return imp.load_source(_module_name, _file_path) except KeyboardInterrupt: print("(Ctrl+C) Exiting!") exit(12) except ImportError as error: if len(possibility.split(os.sep)) > 2: return guess_project_root(possibility, _module_name, error) raise_import_error(error)
def show(self): parser = argparse.ArgumentParser( description='Display current configuration for Test-Junkie', usage="tj config show [OPTIONS]") parser.add_argument( "-a", "--all", action="store_true", default=False, help="Will restore all config settings to its default values") CliUtils.add_standard_boolean_tj_args(parser) args = parser.parse_args(self.args[3:]) if args.all or not self.args[3:]: print("Config is located at: {path}\n".format( path=CliUtils.format_color_string(value=self.config.path, color="green"))) with open(self.config.path, "r") as cfg: print(cfg.read()) return for option, value in args.__dict__.items(): if value is True: self.__print_value(option)
def run_suites(self, args): def tags(): config = { "run_on_match_all": args.run_on_match_all, "run_on_match_any": args.run_on_match_any, "skip_on_match_all": args.skip_on_match_all, "skip_on_match_any": args.skip_on_match_any } for prop, value in config.items(): if value is not None: return config return None if self.suites: print("[{status}] Running tests ...\n".format( status=CliUtils.format_color_string(value="INFO", color="blue"))) try: runner = Runner(suites=self.suites, html_report=args.html_report, xml_report=args.xml_report, config=self.execution_config) runner.run( test_multithreading_limit=args.test_multithreading_limit, suite_multithreading_limit=args.suite_multithreading_limit, tests=args.tests, owners=args.owners, components=args.components, features=args.features, tag_config=tags(), quiet=args.quiet) except KeyboardInterrupt: print("(Ctrl+C) Exiting!") exit(12) except: print("[{status}] Unexpected error during test execution.". format(status=CliUtils.format_color_string(value="ERROR", color="red"))) CliUtils.print_color_traceback() exit(120) finally: if self.coverage is not None: self.coverage.stop() self.coverage.save() import coverage try: print("[{status}] Code coverage report:".format( status=CliUtils.format_color_string(value="INFO", color="blue"))) self.coverage.report(show_missing=True, skip_covered=True) print( "[{status}] TJ uses Coverage.py. Control it with --cov-rcfile, " "see {link}".format( status=CliUtils.format_color_string( value="TIP", color="blue"), link=DocumentationLinks.COVERAGE_CONFIG_FILE)) except coverage.misc.CoverageException: CliUtils.print_color_traceback() exit(120) return
def scan(self): @contextmanager def open_file(_file): if sys.version_info[0] < 3: with open(_file) as _doc: _source = _doc.read() yield (_source, _doc) else: with open(_file, encoding="utf-8") as _doc: _source = _doc.read() yield (_source, _doc) def parse_file(_file): with open_file(_file) as __source: suite_imported_as_alias = re.findall( CliRunner.__REGEX_ALIAS_IMPORT, __source[0]) if suite_imported_as_alias: suite_alias = suite_imported_as_alias[-1].split( "Suite")[-1].split("as")[-1].split(",")[0].strip() CliRunner.start_in_a_thread( target=self.__find_and_register_suite, args=(suite_alias, __source[0], __source[1].name)) return True suite_imported = re.findall(CliRunner.__REGEX_NO_ALIAS_IMPORT, __source[0]) if suite_imported: CliRunner.start_in_a_thread( target=self.__find_and_register_suite, args=("Suite", __source[0], __source[1].name)) return True try: print("\n[{status}] Scanning: {location} ...".format( location=CliUtils.format_color_string(value=",".join( self.sources), color="green"), status=CliUtils.format_color_string(value="INFO", color="blue"))) start = time.time() for source in self.sources: if source.endswith(".py"): parse_file(source) else: for dirName, subdirList, fileList in os.walk(source, topdown=True): if self.__skip(source, dirName): continue for file_path in glob( os.path.join(os.path.dirname(dirName + "\\"), "*.py")): if parse_file(file_path) is True: continue for thread in CliRunner.__SCANNER_THREADS: thread.join() print( "[{status}] Scan finished in: {time} seconds. Found: {suites} suite(s)." .format(status=CliUtils.format_color_string(value="INFO", color="blue"), time="{0:.2f}".format(time.time() - start), suites=CliUtils.format_bold_string(len(self.suites)))) except KeyboardInterrupt: print("(Ctrl+C) Exiting!") exit(12) except BadCliParameters as err: print("[{status}] {error}.".format( status=CliUtils.format_color_string(value="ERROR", color="red"), error=err)) exit(120) except: print("[{status}] Unexpected error during scan for test suites.". format(status=CliUtils.format_color_string(value="ERROR", color="red"))) CliUtils.print_color_traceback() exit(120)
def present_console_output(aggregator): def parse_exception(value): if value is not None: error = "" for line in value.split("\n"): error += "\n\t\t {}".format(line) return error report = aggregator.get_basic_report() test_report = report["tests"] suite_report = report["suites"] for status in TestCategory.ALL: value = "[{part}/{total} {percent}%] {status}".format( part=test_report[status], total=test_report["total"], status=status.upper(), percent=Aggregator.percentage(test_report["total"], test_report[status])) if test_report[status]: value = CliUtils.format_bold_string(value) print(value) print("") for suite, stats in suite_report.items(): status = suite.metrics.get_metrics()["status"] if status is None: # this means that something went wrong with custom event processing status = "*" + SuiteCategory.ERROR print( ">> [{status}] [{passed}/{total} {rate}%] [{runtime:0.2f}s] {module}.{name}" .format( module=CliUtils.format_bold_string( suite.get_class_module()), name=CliUtils.format_bold_string(suite.get_class_name()), status=CliUtils.format_bold_string(status.upper()), runtime=suite.get_runtime(), rate=Aggregator.percentage(stats["total"], stats[TestCategory.SUCCESS]), passed=stats[TestCategory.SUCCESS], total=stats["total"])) if status == SuiteCategory.IGNORE: print("\t|__ reason: {error}".format( error=suite.metrics.get_metrics().get( "initiation_error", None))) if status != SuiteCategory.SUCCESS: tests = suite.get_unsuccessful_tests() for test in tests: test_metrics = test.metrics.get_metrics() print("\t|__ test: {name}()".format( name=CliUtils.format_bold_string( test.get_function_name()))) for class_param, class_param_data in test_metrics.items(): if class_param != "None": print("\t |__ class parameter: {class_parameter}". format(class_parameter=class_param)) for param, param_data in class_param_data.items(): if param != "None": print( "\t |__ parameter: {parameter}".format( parameter=param)) for index in range(param_data["retry"]): trace = param_data["tracebacks"][index] if trace is not None: trace = ":: Traceback: {}".format( CliUtils.format_color_string( parse_exception( trace.encode( 'utf8', errors="replace").decode( "utf8")), "red")) else: trace = "" print( "\t |__ run #{num} [{status}] [{runtime:0.2f}s] {trace}" .format(num=index + 1, trace=trace, runtime=param_data["performance"] [index], status=param_data["statuses"] [index].upper())) print("\n===========================================================") print(". Test Junkie {} (Python{}) {} .".format( pkg_resources.require("test-junkie")[0].version, sys.version_info[0], DocumentationLinks.DOMAIN)) print("===========================================================")
def print_results(self): from test_junkie.cli.cli import CliUtils match_found = False output = [] for data_context in CliAudit.__SECTIONS: if data_context == self.args.command: data = self.aggregated_data["context_by_{context}".format( context=data_context)] if data: section = [] _sorted_data = sorted(data.items(), key=lambda x: x[1]["total_tests"]) _sorted_data.reverse() _sorted_data = collections.OrderedDict(_sorted_data) for primary_key, context in _sorted_data.items(): details = [] if data_context == "suites": details.append( "\nSuite: {value} Feature: {feature}".format( value=CliUtils.format_bold_string( primary_key), feature=CliUtils.format_bold_string( context["feature"]))) else: parent = "".join( list(data_context) [:-1]).capitalize() # exp: features > Feature if primary_key is None: primary_key = CliUtils.format_color_string( primary_key, "red") else: primary_key = CliUtils.format_bold_string( primary_key) details.append("\n{parent}: {value}".format( parent=parent, value=primary_key)) from test_junkie.metrics import Aggregator details.append( "\t- Tests:\t{total} of {absolute} total tests ({percentage}%)" .format(total=context["total_tests"], absolute=self. aggregated_data["absolute_test_count"], percentage=Aggregator.percentage( self. aggregated_data["absolute_test_count"], context["total_tests"]))) for i in CliAudit.__SECTIONS: if i in context: msg = "\t" counter = 0 _sorted_context = sorted(context[i].items(), key=lambda x: x[1]) _sorted_context.reverse() _sorted_context = collections.OrderedDict( _sorted_context) for key, count in _sorted_context.items(): if counter > 0: msg += "\n\t\t\t" if key is None: key = CliUtils.format_color_string( key, "red") msg += "{} ({})".format(key, count) counter += 1 if len(msg) > 0: details.append("\t- {i}: {msg}".format( i=i.capitalize(), msg=msg)) if len(details) >= 5: section += details if len(section) > 1: output.append(section) break for section in output: if len(section) >= 5: match_found = True for msg in section: print(msg) if not match_found: print("[{status}] Nothing matches your search criteria!".format( status=CliUtils.format_color_string("INFO", "blue")))