def _get_test_candidate(version: str, locale: str) -> str or None: """Download and extract a build candidate. Build may either refer to a Firefox release identifier, package, or build directory. :param: build: str with firefox build :return: Installation path for the Firefox App """ logger.debug("Getting build, version %s, locale %s" % (version, locale)) if version == "local": candidate = PathManager.get_local_firefox_path() if candidate is None: logger.critical("Firefox not found. Please download if from https://www.mozilla.org/en-US/firefox/new/") return candidate elif os.path.isfile(version): return version else: try: s_t, s_d = _get_scraper_details( version, CHANNELS, os.path.join(PathManager.get_working_dir(), "cache"), locale ) scraper = FactoryScraper(s_t, **s_d) firefox_dmg = scraper.download() install_dir = install( src=firefox_dmg, dest=os.path.join( PathManager.get_temp_dir(), "firefox{}{}".format(normalize_str(version), normalize_str(locale)) ), ) return get_binary(install_dir, "Firefox") except errors.NotFoundError: logger.critical("Specified build {} has not been found. Closing Iris ...".format(version)) return None
def get_test_params(): tests_to_execute = collect_tests() pytest_args = [] if get_core_args().rerun: failed_tests_file = os.path.join(PathManager.get_working_dir(), "lastfail.txt") tests_dir = os.path.join(PathManager.get_tests_dir(), get_core_args().target) failed_tests = [] with open(failed_tests_file, "r") as f: for line in f: failed_tests.append(line.rstrip("\n")) f.close() # Read first line to see if these tests apply to current target. if tests_dir in failed_tests[0]: pytest_args = failed_tests else: logging.error( "The -a flag cannot be used now because the last failed tests don't match current target." ) else: if len(tests_to_execute) > 0: for running in tests_to_execute: pytest_args.append(running) else: exit_iris("No tests to execute.", status=1) return pytest_args
def check_target(target: str = None): """Checks if provided target exists.""" # Currently not in use, but maintaining for possible future use. if target is None: logger.warning("No target provided.") local_target_path = os.path.join(PathManager.get_module_dir(), "targets") package_target_path = os.path.join(PathManager.get_module_dir(), "moziris", "targets") if os.path.exists(os.path.join(local_target_path, target)): return True if os.path.exists(os.path.join(package_target_path, target)): return True target_list = [] local_path_exists = os.path.exists(local_target_path) package_path_exists = os.path.exists(package_target_path) if local_path_exists: target_list += scan_dir(local_target_path) if package_path_exists: target_list += scan_dir(package_target_path) if not local_path_exists and not package_path_exists: path_warning(local_target_path) else: logger.critical("Target %s doesn't exist.") logger.critical("Did you mean to choose one of these instead?") for target in target_list: logger.critical("\t%s" % target) return False
def make_profile(profile_type: Profiles = None, preferences: dict = None): """Internal-only method used to create profiles on disk. :param profile_type: Profiles.BRAND_NEW, Profiles.LIKE_NEW, Profiles.TEN_BOOKMARKS, Profiles.DEFAULT :param preferences: A dictionary containing profile preferences """ if profile_type is None: profile_type = Profiles.DEFAULT if preferences is None: if profile_type is Profiles.BRAND_NEW: preferences = FirefoxSettings.DEFAULT_FX_PREFS else: preferences = {} test_root = PathManager.get_current_tests_directory() current_test = os.environ.get('CURRENT_TEST') test_path = current_test.split(test_root)[1].split('.py')[0][1:] profile_path = os.path.join(PathManager.get_current_run_dir(), test_path, 'profile') if profile_type is Profiles.BRAND_NEW: logger.debug('Creating brand new profile: %s' % profile_path) elif profile_type in (Profiles.LIKE_NEW, Profiles.TEN_BOOKMARKS): logger.debug('Creating new profile from %s staged profile.' % profile_type.value.upper()) profile_path = FirefoxProfile._get_staged_profile( profile_type, profile_path) else: raise ValueError('No profile found: %s' % profile_type.value) return MozProfile(profile=profile_path, preferences=preferences)
def launch_control_center(): profile_path = os.path.join(PathManager.get_working_dir(), "cc_profile") fx_path = PathManager.get_local_firefox_path() if fx_path is None: logger.error("Can't find local Firefox installation, aborting Iris run.") return False, None args = ["http://127.0.0.1:%s" % get_core_args().port] process_args = {"stream": None} profile = MozProfile(profile=profile_path, preferences=get_fx_prefs()) if OSHelper.is_windows(): process = subprocess.Popen( [ fx_path, "-no-remote", "-new-tab", args, "--wait-for-browser", "-foreground", "-profile", profile.profile, ], shell=False, ) else: fx_runner = FirefoxRunner( binary=fx_path, profile=profile, cmdargs=args, process_args=process_args ) fx_runner.start() logger.debug("Launching web server for directory %s" % PathManager.get_working_dir()) server = LocalWebServer(PathManager.get_working_dir(), get_core_args().port) server.stop() time.sleep(Settings.DEFAULT_UI_DELAY) if OSHelper.is_mac(): type(text="q", modifier=KeyModifier.CMD) elif OSHelper.is_windows(): type(text="w", modifier=[KeyModifier.CTRL, KeyModifier.SHIFT]) else: type(text="q", modifier=KeyModifier.CTRL) if OSHelper.is_windows(): if process.pid is not None: try: logger.debug("Closing Firefox process ID: %s" % process.pid) process = psutil.Process(process.pid) for proc in process.children(recursive=True): proc.kill() process.kill() except psutil.NoSuchProcess: pass else: try: fx_runner.stop() except Exception as e: logger.debug("Error stopping fx_runner") logger.debug(e) return server.result
def collect_tests(): """Collects tests based on include/exclude criteria and selected target.""" target = core_args.target test_list = [] include = core_args.test exclude = core_args.exclude if os.path.isfile(include): with open(include, "r") as f: for line in f: test_list.append(line.rstrip("\n")) f.close() else: tests_dir = os.path.join(PathManager.get_tests_dir(), target) if not os.path.exists(tests_dir): path_warning(tests_dir) return test_list logger.debug("Path %s found. Checking content ...", tests_dir) for dir_path, sub_dirs, all_files in PathManager.sorted_walk( tests_dir): for current_file in all_files: directory = "%s%s%s" % (os.sep, core_args.directory, os.sep) include_params = [include] exclude_params = [exclude] if "," in include: include_params = include.split(",") if "," in exclude: exclude_params = exclude.split(",") current_full_path = os.path.join(dir_path, current_file) if current_file.endswith( ".py") and not current_file.startswith("__"): if include == "" and exclude == "" and directory == "": if current_full_path not in test_list: test_list.append(current_full_path) else: if core_args.directory == "" or directory in current_full_path: for include_param in include_params: if (include_param == "" or include_param in current_full_path): for exclude_param in exclude_params: if exclude_param == "": if current_full_path not in test_list: test_list.append( current_full_path) else: if exclude_param not in current_full_path: if current_full_path not in test_list: test_list.append( current_full_path) if len(test_list) == 0: logger.error( "'%s' does not contain tests based on your search criteria. Exiting program." % tests_dir) else: logger.debug("List of all tests found: [%s]" % ", ".join(map(str, test_list))) return test_list
def scan_all_tests(): tests_directory = PathManager.get_tests_dir() logger.debug("Path %s found. Checking content ...", tests_directory) test_list = {} rootdir = tests_directory.rstrip(os.sep) start = rootdir.rfind(os.sep) + 1 exclude_dirs = {"images", ".pytest_cache", "__pycache__"} exclude_files = {"__init__.py", "pytest.ini", ".DS_Store"} for path, dirs, files in PathManager.sorted_walk(rootdir): [dirs.remove(d) for d in list(dirs) if d in exclude_dirs] [files.remove(d) for d in list(files) if d in exclude_files] folders = path[start:].split(os.sep) subdir = dict.fromkeys(files) parent = reduce(dict.get, folders[:-1], test_list) parent[folders[-1]] = subdir if len(files) > 0: if os.path.isdir(path): my_plugin = TestCollector() pytest.main(["--collect-only", "-p", "no:terminal", path], plugins=[my_plugin]) for module in my_plugin.get_collected_items(): try: module_path = str(module.fspath) module_name = os.path.basename(module_path) temp = module_path.split( "%stests%s" % (os.sep, os.sep))[1].split(module_name)[0] package = os.path.join("tests", temp) current_test = module.own_markers[0].kwargs test_object = { "name": module_name, "module": module_path, "description": current_test.get("description"), "package": package, } if not current_test.get("values"): pass else: for value in current_test.get("values").kwargs: test_object[value] = current_test.get( "values").kwargs[value] subdir[module_name] = test_object except TypeError as e: logger.warning("Error in test - %s: %s" % (module, e.message)) except AttributeError: logger.warning("[%s] is not a test file. Skipping...", module) return test_list
def send_json_report(self): report_s = validate_section('Report_URL') if len(report_s) > 0: logger.warning( '{}. \nJSON report cannot be sent - no report URL found in config file.' .format(report_s)) else: run_file = os.path.join(PathManager.get_current_run_dir(), 'run.json') url = get_config_property('Report_URL', 'url') if url is not None: try: with open(run_file, 'rb') as file: r = requests.post(url=url, files={'file': file}) if not r.ok: logger.error( 'Report was not sent to URL: %s \nResponse text: %s' % url, r.text) logger.debug('Sent JSON report status: %s' % r.text) except requests.RequestException as ex: logger.error( 'Failed to send run report to URL: %s \nException data: %s' % url, ex) else: logger.error('Bad URL for JSON report.')
def save_failed_tests(test_list): failed_tests_file = os.path.join(PathManager.get_working_dir(), "lastfail.txt") with open(failed_tests_file, "w") as f: for test in test_list: f.write(test + "\n") f.close()
def send_json_report(self): report_s = validate_section("Report_URL") if len(report_s) > 0: logger.warning( "{}. \nJSON report cannot be sent - no report URL found in config file." .format(report_s)) else: run_file = os.path.join(PathManager.get_current_run_dir(), "run.json") url = get_config_property("Report_URL", "url") if url is not None: try: with open(run_file, "rb") as file: r = requests.post(url=url, files={"file": file}) if not r.ok: logger.error( "Report was not sent to URL: %s \nResponse text: %s" % url, r.text) logger.debug("Sent JSON report status: %s" % r.text) except requests.RequestException as ex: logger.error( "Failed to send run report to URL: %s \nException data: %s" % url, ex) else: logger.error("Bad URL for JSON report.")
def __init__(self): BaseTarget.__init__(self) global target_args target_args = self.get_target_args() self.target_name = "Firefox" self.process_list = [] self.cc_settings = [ { "name": "firefox", "type": "list", "label": "Firefox", "value": ["local", "latest", "latest-esr", "latest-beta", "nightly"], "default": "beta", }, { "name": "locale", "type": "list", "label": "Locale", "value": OSHelper.LOCALES, "default": "en-US" }, { "name": "max_tries", "type": "list", "label": "Maximum tries per test", "value": ["1", "2", "3", "4", "5"], "default": "3", }, { "name": "highlight", "type": "checkbox", "label": "Debug using highlighting" }, { "name": "override", "type": "checkbox", "label": "Run disabled tests" }, { "name": "email", "type": "checkbox", "label": "Email results" }, { "name": "report", "type": "checkbox", "label": "Create TestRail report" }, ] self.local_web_root = os.path.join(PathManager.get_module_dir(), "targets", "firefox", "local_web") if target_args.treeherder: Settings.debug_image = False
def initialize_logger(): logging.basicConfig(filename=PathManager.get_log_file_path(), format=set_log_format()) initialize_logger_level(core_args.level) # Control pytest terminal output via environment variable instead of parameter. if core_args.level > 10: os.environ["PYTEST_ADDOPTS"] = "-p no:terminal"
def update_run_index(app, finished=False): if finished: failed = 0 total_duration = 0 for test in app.completed_tests: if test.outcome == "FAILED" or test.outcome == "ERROR": failed = failed + 1 total_duration = total_duration + test.test_duration current_run = { "duration": total_duration, "failed": failed, "id": PathManager.get_run_id(), "locale": args.locale, "target": args.target, "total": len(app.completed_tests), } else: current_run = { "duration": "-1", "failed": "-1", "id": PathManager.get_run_id(), "locale": args.locale, "target": args.target, "total": "-1", } run_file = os.path.join(PathManager.get_working_dir(), "data", "runs.json") if os.path.exists(run_file): logger.debug("Updating run file: %s" % run_file) with open(run_file, "r") as f: run_file_data = json.load(f) for run in run_file_data["runs"]: if run["id"] == PathManager.get_run_id(): run_file_data["runs"].remove(run) run_file_data["runs"].append(current_run) else: logger.debug("Creating run file: %s" % run_file) run_file_data = {"runs": []} run_file_data["runs"].append(current_run) with open(run_file, "w") as f: json.dump(run_file_data, f, sort_keys=True, indent=True) f.close()
def exit_iris(message, status=0): if status == 0: logger.info(message) elif status == 1: logger.error(message) else: logger.debug(message) delete(PathManager.get_run_id(), update_run_file=False) ShutdownTasks.at_exit() exit(status)
def _get_staged_profile(profile_name, path): """ Internal-only method used to extract a given profile. :param profile_name: :param path: :return: """ staged_profiles = os.path.join(PathManager.get_module_dir(), "targets", "firefox", "firefox_app", "profiles") sz_bin = find_executable("7z") logger.debug('Using 7zip executable at "%s"' % sz_bin) zipped_profile = os.path.join(staged_profiles, "%s.zip" % profile_name.value) cmd = [sz_bin, "x", "-y", "-bd", "-o%s" % staged_profiles, zipped_profile] logger.debug('Unzipping profile with command "%s"' % " ".join(cmd)) try: output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: logger.error("7zip failed: %s" % repr(e.output)) raise Exception("Unable to unzip profile.") logger.debug("7zip succeeded: %s" % repr(output)) from_directory = os.path.join(staged_profiles, profile_name.value) to_directory = "%s_%s" % (path, datetime.datetime.now().strftime("%Y%m%d%H%M%S")) if os.path.exists(path): try: shutil.rmtree(path) time.sleep(3) except Exception as e: logger.debug("Error, can't remove previous profile: %s" % e) logger.debug("Creating new profile: %s" % to_directory) try: logger.debug("From directory: %s" % from_directory) logger.debug("To directory: %s" % to_directory) dir_util.copy_tree(from_directory, to_directory) except Exception as e: logger.error("Error upon creating profile: %s" % e) try: shutil.rmtree(from_directory) except WindowsError: logger.debug("Error, can't remove orphaned directory, leaving in place.") resource_fork_folder = os.path.join(staged_profiles, "__MACOSX") if os.path.exists(resource_fork_folder): try: shutil.rmtree(resource_fork_folder) except WindowsError: logger.debug("Error, can't remove orphaned directory, leaving in place.") return to_directory
def create_target_json(): if not use_cached_target_file(): logging.info("Preparing data for the Control Center.") logging.info("This may take a minute.") if os.path.exists(os.path.join(PathManager.get_module_dir(), "targets")): master_target_dir = os.path.join(PathManager.get_module_dir(), "targets") else: master_target_dir = os.path.join(Settings.PACKAGE_ROOT, "moziris", "targets") target_list = [ f for f in os.listdir(master_target_dir) if not f.startswith("__") and not f.startswith(".") ] targets = [] for item in target_list: try: target_tests = scan_all_tests(item) target_module = importlib.import_module("targets.%s.main" % item) try: target = target_module.Target() targets.append({ "name": target.target_name, "tests": target_tests, "icon": "%s.png" % item, "settings": target.cc_settings, }) except NameError: logger.error("Can't find default Target class.") except ImportError as e: logger.error("Problems importing module '%s':\n%s" % (item, e)) target_json = {"targets": targets} target_json_file = os.path.join(PathManager.get_working_dir(), "data", "targets.json") with open(target_json_file, "w") as f: json.dump(target_json, f, sort_keys=True, indent=True)
def submit_email_report(target, result): """ PLACEHOLDER FOR EMAIL REPORT :param test_results: TEST RESULT SESSION need to update with appliications and git object """ logger.info( " --------------------------------------------------------- " + Color.BLUE + "Starting Email report:" + Color.END + " ----------------------------------------------------------\n") email_report = EmailClient() email_report.send_email_report(target, str(result), PathManager.get_git_details())
def init_control_center(): cc_assets_path = os.path.join( os.path.realpath(os.path.split(__file__)[0] + "/.."), "control_center", "assets" ) logger.debug( "Copying Control Center assets from %s to %s" % (cc_assets_path, PathManager.get_working_dir()) ) copy_tree(cc_assets_path, PathManager.get_working_dir()) if os.path.exists(os.path.join(PathManager.get_module_dir(), "targets")): logger.debug("Looking for CC files in module directory.") targets_dir = os.path.join(PathManager.get_module_dir(), "targets") else: logger.debug("Looking for CC files in package directory.") targets_dir = os.path.join(Settings.PACKAGE_ROOT, "moziris", "targets") exclude_dirs = {"__pycache__"} for path, dirs, files in PathManager.sorted_walk(targets_dir): [dirs.remove(d) for d in list(dirs) if d in exclude_dirs] for target in dirs: src = os.path.join(targets_dir, target, "icon.png") dest = os.path.join(PathManager.get_working_dir(), "images", "%s.png" % target) try: shutil.copyfile(src, dest) except FileNotFoundError: logger.warning("Could not find icon file for target: %s" % target) break create_target_json()
def delete_all(): """ Delete each run in the runs.json file, one at a time. """ logger.debug("Delete All command received.") run_file = os.path.join(PathManager.get_working_dir(), "data", "runs.json") with open(run_file, "r") as data: run_file_data = json.load(data) data.close() for run in run_file_data["runs"]: delete(run["id"])
def delete(args, update_run_file=True): """ Delete a past run. :param args: The run ID to delete :param update_run_file: Remove entry from runs.json file """ logger.debug("Received delete command with arguments: %s " % args) if update_run_file: # Load run log JSON, find entry that matches the argument and delete it. # Then, write new run log file. run_file = os.path.join(PathManager.get_working_dir(), "data", "runs.json") if os.path.exists(run_file): logger.debug("Deleting entry %s from run file: %s" % (args, run_file)) with open(run_file, "r") as data: run_file_data = json.load(data) found = False for run in run_file_data["runs"]: if run["id"] == args: run_file_data["runs"].remove(run) found = True if found: with open(run_file, "w") as data: json.dump(run_file_data, data, sort_keys=True, indent=True) else: logger.error("Entry for run %s not found in run log file." % args) else: logger.error("Run file not found.") # Remove run directory on disk. target_run = os.path.join(PathManager.get_working_dir(), "runs", args) if os.path.exists(target_run): shutil.rmtree(target_run, ignore_errors=True) else: logger.debug("Run directory does not exist: %s" % target_run)
def scan_all_tests(target): logging.info("Gathering test info for '%s'..." % target) master_test_root = os.path.join(PathManager.get_module_dir(), "tests") test_root = os.path.join(master_test_root, target) base_props = ["description"] tests = [] my_plugin = TestCollector() pytest.main(["--collect-only", "-s", "-p", "no:terminal", test_root], plugins=[my_plugin]) for test in my_plugin.get_collected_items(): original_path = str(test.__dict__.get("fspath")) target_root = original_path.split(master_test_root)[1] test_path = target_root.split("%s%s%s" % (os.sep, target, os.sep))[1] parent = tests details = get_test_markers(test) for module in test_path.split(os.sep): test_obj = {"name": module.split(".py")[0]} if "py" not in module: module_exists = False for objects in parent: if objects["name"] == module: parent = objects["children"] module_exists = True break if not module_exists: new_parent = test_obj["children"] = [] parent.append(test_obj) parent = new_parent else: for prop in base_props: if details.get(prop) is not None: test_obj[prop] = details.get(prop) else: test_obj[prop] = "" test_obj["file"] = original_path values = {} for i in details: if i not in base_props: values[i] = details.get(i) if details.get("platform") is None: values["platform"] = "all" if details.get("locale") is None: values["locale"] = "all" test_obj["values"] = values parent.append(test_obj) parent = tests return tests
def get_file_attachment(): test_report_file = os.path.join(PathManager.get_current_run_dir(), "iris_log.log") if os.path.exists(test_report_file): file_log = open(test_report_file) attachment = MIMEText(file_log.read(), 1) file_log.close() attachment.add_header( "Content-Disposition", "attachment", filename=os.path.basename(test_report_file), ) return attachment else: raise Exception("File %s is not present in path" % test_report_file)
def __init__(self): BaseTarget.__init__(self) global target_args target_args = self.get_target_args() self.target_name = 'Nightly' self.process_list = [] self.cc_settings = [{ 'name': 'nightly', 'type': 'list', 'label': 'Nightly', 'value': ['local', 'latest', 'nightly'], 'default': 'nightly' }, { 'name': 'locale', 'type': 'list', 'label': 'Locale', 'value': OSHelper.LOCALES, 'default': 'en-US' }, { 'name': 'max_tries', 'type': 'list', 'label': 'Maximum tries per test', 'value': ['1', '2', '3', '4', '5'], 'default': '3' }, { 'name': 'highlight', 'type': 'checkbox', 'label': 'Debug using highlighting' }, { 'name': 'override', 'type': 'checkbox', 'label': 'Run disabled tests' }, { 'name': 'email', 'type': 'checkbox', 'label': 'Email results' }, { 'name': 'report', 'type': 'checkbox', 'label': 'Create TestRail report' }] self.local_web_root = os.path.join(PathManager.get_module_dir(), 'targets', 'firefox', 'local_web') if target_args.treeherder: Settings.debug_image = False
def use_cached_target_file(): """ Helper function to determine if target.json is relatively recent and can be re-used. :return: True/False """ # We will cache the target file for t5 minutes - adjust below if desired. cache_time = 60 * 15 result = False target_json_file = os.path.join(PathManager.get_working_dir(), "data", "targets.json") if os.path.exists(target_json_file): file_modified_time = int(os.path.getmtime(target_json_file)) logger.debug("Target file created: %s" % file_modified_time) current_time = int(time.mktime(datetime.now().timetuple())) logger.debug("Current time: %s" % current_time) elapsed_time = current_time - file_modified_time logger.debug("Elapsed time: %s" % elapsed_time) if elapsed_time < cache_time: result = True return result
def _get_staged_profile(profile_name, path): """ Internal-only method used to extract a given profile. :param profile_name: :param path: :return: """ staged_profiles = os.path.join(PathManager.get_module_dir(), 'targets', 'firefox', 'firefox_app', 'profiles') sz_bin = find_executable('7z') logger.debug('Using 7zip executable at "%s"' % sz_bin) zipped_profile = os.path.join(staged_profiles, '%s.zip' % profile_name.value) cmd = [ sz_bin, 'x', '-y', '-bd', '-o%s' % staged_profiles, zipped_profile ] logger.debug('Unzipping profile with command "%s"' % ' '.join(cmd)) try: output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: logger.error('7zip failed: %s' % repr(e.output)) raise Exception('Unable to unzip profile.') logger.debug('7zip succeeded: %s' % repr(output)) from_directory = os.path.join(staged_profiles, profile_name.value) to_directory = '%s_%s' % ( path, datetime.datetime.now().strftime('%Y%m%d%H%M%S')) if os.path.exists(path): try: shutil.rmtree(path) time.sleep(3) except Exception as e: logger.debug('Error, can\'t remove previous profile: %s' % e) logger.debug('Creating new profile: %s' % to_directory) try: logger.debug('From directory: %s' % from_directory) logger.debug('To directory: %s' % to_directory) dir_util.copy_tree(from_directory, to_directory) except Exception as e: logger.error('Error upon creating profile: %s' % e) try: shutil.rmtree(from_directory) except WindowsError: logger.debug( 'Error, can\'t remove orphaned directory, leaving in place.') resource_fork_folder = os.path.join(staged_profiles, '__MACOSX') if os.path.exists(resource_fork_folder): try: shutil.rmtree(resource_fork_folder) except WindowsError: logger.debug( 'Error, can\'t remove orphaned directory, leaving in place.' ) return to_directory
def pytest_runtest_setup(self, item): os.environ["CURRENT_TEST"] = str(item.__dict__.get("fspath")) Settings.debug_image_path = PathManager.get_debug_image_directory()
def create_run_log(app): args = get_core_args() meta = { "run_id": PathManager.get_run_id(), "platform": OSHelper.get_os().value, "config": "%s, %s-bit, %s" % (OSHelper.get_os_version(), OSHelper.get_os_bits(), OSHelper.get_processor()), "locale": args.locale, "args": " ".join(sys.argv), "params": vars(args), "log": os.path.join(PathManager.get_current_run_dir(), "iris_log.log"), } values = {} for i in app.values: values[i] = app.values[i] meta["values"] = values meta["iris_version"] = 2.0 try: repo = git.Repo(PathManager.get_module_dir()) meta["iris_repo"] = repo.working_tree_dir try: meta["iris_branch"] = repo.active_branch.name except: # If we're on a detached head, the active_branch is # undefined and raises an exception. This at least # allows the test run to finish meta["iris_branch"] = "detached" meta["iris_branch_head"] = repo.head.object.hexsha except: # Iris is not running in a Git repo, so don't try to # report on non-existent data. meta["iris_repo"] = "n/a" meta["iris_branch"] = "n/a" meta["iris_branch_head"] = "n/a" meta["python_version"] = get_python_version() failed = 0 passed = 0 skipped = 0 errors = 0 for test in app.completed_tests: if test.outcome == "FAILED": failed = failed + 1 if test.outcome == "PASSED": passed = passed + 1 if test.outcome == "SKIPPED": skipped = skipped + 1 if test.outcome == "ERROR": errors = errors + 1 logger.debug("Updating run.json with completed run data.") meta["total"] = len(app.completed_tests) meta["passed"] = passed meta["failed"] = failed meta["skipped"] = skipped meta["errors"] = errors meta["start_time"] = app.start_time meta["end_time"] = app.end_time meta["total_time"] = app.end_time - app.start_time tests = { "all_tests": convert_test_list(app.completed_tests), "failed_tests": convert_test_list(app.completed_tests, only_failures=True), "flaky_tests": app.flaky_tests, } run_file = os.path.join(PathManager.get_current_run_dir(), "run.json") run_file_data = {"meta": meta, "tests": tests} with open(run_file, "w") as f: json.dump(run_file_data, f, sort_keys=True, indent=True)
def convert_test_list(test_list, only_failures=False): """Takes a flat list of test objects and paths and converts to an object that can be serialized as JSON. :param test_list: List of completed tests :param only_failures: If True, only return failed tests :return: """ test_root = os.path.join(PathManager.get_module_dir(), "tests") tests = [] for test in test_list: test_failed = (True if "FAILED" in test.outcome or "ERROR" in test.outcome else False) original_path = str(test.item.__dict__.get("fspath")) try: target_root = original_path.split(test_root)[1] except IndexError: logger.error("Error parsing test list.") logger.error( "Try resetting your PYTHONPATH before your next run, i.e.:") if OSHelper.get_os().value == "win": logger.error("\tsetx PYTHONPATH %CD%") else: logger.error("\texport PYTHONPATH=$PWD") return tests target = target_root.split(os.sep)[1] test_path = target_root.split("%s%s%s" % (os.sep, target, os.sep))[1] parent = tests details = get_test_markers(test.item) for module in test_path.split(os.sep): test_obj = {"name": module.split(".py")[0]} if "py" not in module: module_exists = False for objects in parent: if objects["name"] == module: parent = objects["children"] module_exists = True break if not module_exists: new_parent = test_obj["children"] = [] if only_failures and test_failed: parent.append(test_obj) elif not only_failures: parent.append(test_obj) parent = new_parent else: if test_failed: test_assert = { "error": test.error.lstrip(), "message": test.message.lstrip(), "call_stack": test.traceback + "\n\n ", "code": get_failing_code(test.node_name, int(test.line)), } test_obj["assert"] = test_assert test_obj["result"] = test.outcome test_obj["time"] = test.test_duration debug_image_directory = os.path.join( PathManager.get_current_run_dir(), test_path.split(".py")[0], "debug_images", ) test_obj["debug_image_directory"] = debug_image_directory test_obj["debug_images"] = get_image_names( debug_image_directory) test_obj["description"] = details.get("description") values = {} for i in details: if i != "description": values[i] = details.get(i) test_obj["values"] = values if only_failures and test_failed: parent.append(test_obj) elif not only_failures: parent.append(test_obj) parent = tests return tests
def at_exit(): reset_terminal_encoding() if os.path.exists(PathManager.get_temp_dir()): shutil.rmtree(PathManager.get_temp_dir(), ignore_errors=True)
def initialize_platform(args): init() fix_terminal_encoding() migrate_data() PathManager.create_working_directory() PathManager.create_run_directory()