def main(): log_new('_main') parameters = get_parameters() config = Config() prepare_dir_structure(config) build_number = str(parameters.build_number) check_build_status(build_number, config) artifact_file_name = get_teamcity_artifact_file_name(build_number, config) artifact_url = get_teamcity_artifact_url(build_number, artifact_file_name, config) artifact_file = os.path.join(config.TEMP_ARTIFACTS_DIR, artifact_file_name) download_teamcity_artifact(artifact_url, artifact_file) unzip(artifact_file, config.TEMP_ARTIFACTS_DIR) check_artifacts(config.TEMP_ARTIFACTS_DIR, config) delete_unnecessary_artifacts(config.TEMP_ARTIFACTS_DIR, config) rename_artifacts(build_number, config.TEMP_ARTIFACTS_DIR) return 0
def compare_images(process_list, diff_dir, rgb_threshold, size_threshold, config): """ Image comparison: cycle through list of image pairs creating corresponding diffs :param process_list: list of image pairs (paths) :type process_list: [(list[str], list[str])] :param diff_dir: target diff directory :type diff_dir: str :param rgb_threshold: color filter threshold value in rgba units :type rgb_threshold: int :param size_threshold: size filter threshold value in pixels :type size_threshold: int :param config: configuration :type config: Config :return: namedtuple containing paths of initial images and created diff (l_image_path, r_image_path, diff_path) :rtype: list[ImageSet] """ log_new('compare_images') assert isinstance(process_list, list) assert isinstance(diff_dir, str) return [ ImageSet( correct_name, l_image_path, r_image_path, _compare_pair(l_image_path, r_image_path, diff_dir, rgb_threshold, size_threshold, config.WHITENING_RATIO)) for correct_name, l_image_path, r_image_path in process_list ]
def adb_load_base(config, parameters): log_new('load_base') [ base, device_name, ] = parameters base_name = os.path.basename(base) logg('base', base) logg('device_name', device_name) adb_client = get_adb_client() adb_device = AdbDevice(device_name, config, adb_client) adb_device.echo('Recreating work dir {}'.format(config.ANDROID_WORK_DIR)) if config.ANDROID_WORK_DIR == config.ANDROID_MAIN_DIR: raise Exception('Incorrect work dir: {}'.format( config.ANDROID_WORK_DIR)) adb_device.delete_path_on_android(config.ANDROID_WORK_DIR) adb_device.create_dir_on_android(config.ANDROID_WORK_DIR) adb_device.echo('Loading base to Android device: {}'.format(device_name)) adb_device.copy_file_to_android( base, '{}/{}'.format(config.ANDROID_WORK_DIR, base_name)) adb_device.list_dir_content_on_android(config.ANDROID_WORK_DIR) adb_device.echo('Loading base finished')
def _get_parameters(): log_new('_get_parameters') logg('cl', sys.argv) parser = argparse.ArgumentParser() parser.add_argument("--current-results-build", type=str, required=False) #left for backward compatability parser.add_argument("--baseline-results-build", type=str, required=False) #left for backward compatability parser.add_argument("--current-build", type=str, required=True) parser.add_argument("--baseline-build", type=str, required=True) parser.add_argument("--base-name", type=str, required=True) parser.add_argument("--test-name", type=str, required=True) parser.add_argument("--devices", type=str, required=True) parser.add_argument("--rgb-filter-threshold", type=str, required=True) parser.add_argument("--size-filter-threshold", type=str, required=True) parser.add_argument("--test-branch", type=str, required=True) parser.add_argument("--recreate-artifacts", type=str, default="false", required=True) parser.add_argument("--qa-tcserver", type=str, required=True) parameters = parser.parse_args() logg('parameters', parameters) return parameters
def unzip(archive, path): log_new('unzip') logg('archive', archive) logg('path', path) with zipfile.ZipFile(archive, "r") as z: z.extractall(path)
def check_result_file(cb, result_name): log_new('check_result_file') def is_existing_file(path): log_new('is_existing_file') return os.path.exists(path) and os.path.isfile(path) if 'current' in cb: cb_build = os.path.split(parameters.current_dir)[-1] cb_dir = parameters.current_dir elif 'baseline' in cb: cb_build = os.path.split(parameters.baseline_dir)[-1] cb_dir = parameters.baseline_dir if result_name == 'scenario': result_file_name = '_'.join(( result_name, parameters.test_name, parameters.city_name )) + config.ARTIFACT_FORMAT else: result_file_name = '_'.join(( result_name, parameters.device_name, parameters.test_name, parameters.city_name, cb_build )) + config.ARTIFACT_FORMAT result_file = os.path.join(cb_dir, result_file_name) logg('result_file', result_file) assert is_existing_file(result_file), 'result file not found: {}'.format(result_file)
def get_tags(tcserver_info, build_id): log_new('get_tags') url = ''.join(( tcserver_info.url, '/app/rest/builds/buildType:', tcserver_info.project, ',id:', build_id, '/tags/', )) logg('url', url) response = requests.get(url, auth=(tcserver_info.login, tcserver_info.password)) answer = response.content logg('answer', answer) if response.status_code != 200: raise Exception( 'Unable to get builds information from server, status code: {}'. format(str(response.status_code))) tags = parse_xml_answer(answer, "//tag/@name") return tags
def _run_results_comparison(device_name, parameters, config, current_build_number, baseline_build_number, current_results_build_number, baseline_results_build_number, qa_tcserver_info2): log_new('_run_results_comparison') base_name = parameters.base_name test_name = parameters.test_name rgb_threshold = str(parameters.rgb_filter_threshold) size_threshold = str(parameters.size_filter_threshold) test_branch = parameters.test_branch qa_tc_project2_parameters = { '01_current_results_build': current_results_build_number, '02_baseline_results_build': baseline_results_build_number, '03_current_build_number': current_build_number, '04_baseline_build_number': baseline_build_number, '05_device_name': device_name, '06_test_name': test_name, '07_base_name': base_name, '08_rgb_threshold': rgb_threshold, '09_size_threshold': size_threshold, } qa_comment = _create_comment( device_name, test_name, base_name, '{} vs {}'.format(current_build_number, baseline_build_number)) comparison_build_id = _run_build(config, qa_tcserver_info2, qa_tc_project2_parameters, test_branch, qa_comment, None) build_numbers = _wait_builds(config, qa_tcserver_info2, [comparison_build_id], [qa_comment]) return build_numbers[comparison_build_id]
def run_process(config, cl): log_new('run_process') def wait_process(process, timeout): log_new('wait_process') start_time = time.time() wait_key = True while wait_key: time.sleep(config.PROCESS_WAIT_TIME_STEP_SEC) if process.poll() is None: # process is running / not terminated current_time = time.time() seconds_passed = int(current_time - start_time) if seconds_passed > int(timeout): log(' '.join(('timeout:', str(seconds_passed), 'sec'))) wait_key = False subprocess.call( ['taskkill', '/F', '/T', '/PID', str(process.pid)]) log('process terminated') else: wait_key = False logg('cl', cl) process = subprocess.Popen(cl) wait_process(process, config.SHORT_TIMEOUT_SEC)
def list_dir_content_on_android(self, dir_path): log_new('list_dir_content_on_android') logg('dir_path', dir_path) cl = 'ls {}'.format(dir_path) log('-------------------') self._execute_shell_command_on_device(cl) log('-------------------')
def get_test_data(parameters, config): log_new('prepare_test_data') artifacts = select_artifact(parameters.device_name, config) build_base = add_build_to_filename(artifacts.base, config.ARTIFACT_EXTENSIONS, parameters.build_number) build_dynamic_layout = add_build_to_filename( artifacts.dynamic_layout, config.ARTIFACT_EXTENSIONS, parameters.build_number) build_app = add_build_to_filename(artifacts.app, config.ARTIFACT_EXTENSIONS, parameters.build_number) scenario_name = '.'.join((parameters.test_name, 'json')) device_config_name = config.DEFAULT_DEVICE_CONFIG.replace('device', str(parameters.device_name)) test_data = Data( base=os.path.join(config.TEMP_DATA_DIR, build_base), app_src=os.path.join(config.TEMP_DATA_DIR, build_app), scenario_name=scenario_name, scenario_src=os.path.join(config.ENV_TESTS_DIR, scenario_name), sh_script_src=os.path.join(config.ENV_SHELL_SCRIPTS_DIR, config.ANDROID_SIDE_RUN_SCRIPT_NAME), wait_script_src=os.path.join(config.ENV_PROJECT_DIR, config.WAIT_SCRIPT_NAME), result_screens_dir=os.path.join(config.TEMP_ARTIFACTS_DIR, 'screens'), result_logs_dir=os.path.join(config.TEMP_ARTIFACTS_DIR, 'logs'), device_config_src=os.path.join(config.ENV_CONFIG_DIR, device_config_name), device_config_name=device_config_name, log_name=config.LOG_NAME.replace('build', str(parameters.build_number)), dynamic_layout=os.path.join(config.TEMP_DATA_DIR, build_dynamic_layout), ) logg('test_data', test_data) return test_data
def check_artifacts(artifact_path, config): log_new('_check_artifacts') artifacts = config.ARTIFACTS + config.ARTIFACTS_X86 artifact_files = os.listdir(artifact_path) not_found_files = filter(lambda name: name not in artifact_files, artifacts) assert len(not_found_files) is 0, 'Artifact files not found: {}'.format(not_found_files)
def adb_restart(config): log_new('adb_restart') # use only at global start! kill_adb(config) start_adb(config) reconnect_offline(config)
def _set_comment(build_number, comment, tcserver_info): log_new('_set_comment') builds_info = get_builds(tcserver_info, build_number) build_info = builds_info[build_number] old_comment = build_info.comment build_id = build_info.id new_comment = '{}\n{}'.format(old_comment, comment) add_comment(build_id, new_comment, tcserver_info)
def main(): log_new('_main') config = Config() parameters = get_parameters() check_input_data(parameters, config) return 0
def remove_files_if(func, files): log_new('remove_files_if') for file in files: if func(file): if os.path.isfile(file): logg('deleting file', file) os.remove(file) else: delete_directory(file)
def start_build(tcserver_info, tc_project_parameters_dict, branch, comment): log_new('start_build') url = tcserver_info.url + '/app/rest/buildQueue/' logg('url', url) parameters_row = '' for parameter_name, parameter_value in tc_project_parameters_dict.iteritems( ): parameters_row += '<property name="{}" value="{}"/>'.format( parameter_name, parameter_value) tc_project_parameters = '<properties>{}</properties>'.format( parameters_row) comment = '<comment><text>{}</text></comment>'.format(comment) if 'default' in branch.lower(): branch = escape(branch) template = '<build branchName="{}"><buildType id={}/>{}{}</build>'.format( branch, '{id}', tc_project_parameters, comment) logg('template', template) headers = {'Content-Type': 'application/xml'} data = template.format(id=quoteattr(tcserver_info.project)) logg('data', data) logg('login | password', '{} | {}'.format(tcserver_info.login, tcserver_info.password)) response = requests.post(url, headers=headers, data=data, auth=(tcserver_info.login, tcserver_info.password), timeout=10) answer = response.content logg('answer', answer) if response.status_code != 200: raise Exception('Unable to start build, status code: {}'.format( str(response.status_code))) doc = etree.fromstring(answer.strip()) values = doc.xpath("//build/@id") build_id = values[0] logg('values', values) logg('build_id', build_id) return build_id
def _run_build(config, qa_tcserver_info, qa_tc_project1_parameters_current, test_branch, qa_comment, remote_build_number): log_new('_run_build') build_id = start_build(qa_tcserver_info, qa_tc_project1_parameters_current, test_branch, qa_comment) if build_id is None: _finalize_build(remote_build_number, False, config, qa_tcserver_info) raise Exception('Build not started!') return build_id
def _check_build_existence(build_number, tcserver_info): log_new('_check_build_existence') builds_info = get_builds(tcserver_info, build_number) if not builds_info: #True if dict is empty raise Exception('Build #{} not found with scope depth {}'.format( build_number, tcserver_info.scope)) else: log('Build #{} exists'.format(build_number))
def get_parameters(): log_new('_get_parameters') logg('cl', sys.argv) parser = argparse.ArgumentParser() parser.add_argument("-b", "--build-number", type=str, required=True) args = parser.parse_args() logg('args', args) return args
def get_build_url(build_number, build_fields): log_new('get_build_url') query_parameters = [ ['fields', 'build(' + ','.join(build_fields) + ')'], ['locator', 'number:' + build_number], ] path = config.REMOTE_TC_SERVER_URL + '/app/rest/buildTypes/id:' + config.REMOTE_TC_PROJECT + '/builds' query = '&'.join(map(lambda pair: pair[0] + '=' + pair[1], query_parameters)) url = path + '?' + query return url
def prepare_dir_structure(parameters, config): log_new('prepare_dir_structure') screens_diff_dir = os.path.join(parameters.diff_dir, 'screens') prepare_directory(screens_diff_dir) prepare_directory(config.TEMP_ARTIFACTS_DIR) return screens_diff_dir
def _finalize_build(build_number, is_qa_success, config, tcserver_info): log_new('_finalize_build') assert (build_number is not None) builds_info = get_builds(tcserver_info, build_number) if is_qa_success: _set_finished_tag(build_number, builds_info, config, tcserver_info) else: _set_broken_tag(build_number, builds_info, config, tcserver_info)
def _set_tag(chosen_build, builds_info, del_tags, new_tag, tcserver_info): log_new('_set_tag') log('{} -> {}'.format(del_tags, new_tag)) build_info = builds_info[chosen_build] old_tags = set(build_info.tags) logg('old_tags', old_tags) tags_to_delete = set(del_tags) new_tags = (old_tags - tags_to_delete) | {new_tag} add_tags(chosen_build, new_tags, tcserver_info)
def check_input_artifacts(artifacts_path, build_number, device, config): log_new('check_input_artifacts') logg('artifacts_path', artifacts_path) artifacts = select_artifact(device, config) artifact_files = os.listdir(artifacts_path) expected_artifact_files = map(lambda name: add_build_to_filename(name, config.ARTIFACT_EXTENSIONS, build_number), artifacts) logg('expected_artifact_files', expected_artifact_files) not_found_files = filter(lambda name: name not in artifact_files, expected_artifact_files) assert len(not_found_files) is 0, 'Artifact files not found: {}'.format(not_found_files)
def get_teamcity_artifact_file_name(build_number, config): log_new('get_artifact_file_name') artifact_file_name = ''.join(( config.ARTIFACT_NAME, '-', build_number, config.ARTIFACT_FORMAT )) logg('artifact_file_name', artifact_file_name) return artifact_file_name
def get_parameters(): log_new('_get_parameters') logg('cl', sys.argv) parser = argparse.ArgumentParser() parser.add_argument("-b", "--build-number", type=str, required=True) parser.add_argument("-d", "--device-name", type=str, required=True) parser.add_argument("-t", "--test-name", type=str, required=True) parameters = parser.parse_args() logg('parameters', parameters) return parameters
def delete_unnecessary_artifacts(artifact_path, config): log_new('delete_unnecessary_artifacts') artifact_files = map(lambda name: os.path.join(artifact_path, name), os.listdir(artifact_path)) artifacts = config.ARTIFACTS + config.ARTIFACTS_X86 @logging def delete_condition(name): return os.path.split(name)[1] not in artifacts remove_files_if(delete_condition, artifact_files)
def parse_response(response, build_fields): log_new('parse_response') response_content = response.content root = etree.fromstring(response_content) node = root.xpath('/builds/build')[0] result = {} for field_name in build_fields: value = node.get(field_name) logg(field_name, value) assert value is not None result[field_name] = value return result
def get_teamcity_artifact_url(build_number, artifact_file_name, config): log_new('get_artifact_url') url = '/'.join(( config.REMOTE_TC_SERVER_GUEST_URL, 'repository/download', config.REMOTE_TC_PROJECT, build_number, artifact_file_name )) logg('url created', url) return url