예제 #1
0
def found_global_report(g_reports_l):
    """
    Found valid global report

    :param g_reports_l: potential global reports list
    :type g_reports_l: list of paths
    :return: global report path
    :rtype: string
    """
    reports = []
    log.debug('found_global_report')
    log.debug(g_reports_l)
    for report in g_reports_l:
        valid = True
        report_json = json.load(open(report, 'r'))
        for task in report_json:
            # Check if 'steps' key is there
            try:
                steps = task['steps']
                log.debug(steps)
            except (KeyError, TypeError):
                log.info('%s not a valid format for global report', report)
                valid = False
                break
        if valid:
            reports.append(report)

    return reports
예제 #2
0
def parse_global_report(global_report):
    """
    Parse global json report

    :param global_report: global report path
    :type global_report: string
    :return failed_steps: all failed steps
    :rtype: dictionary
    """
    failed_steps = {}

    report_json = json.load(open(global_report, 'r'))
    log.info(report_json)
    for task in report_json:
        steps = task.get('steps')
        for step in steps:
            if step.get('failed'):
                step = step['step_name']
                infos = task.get('task_infos')
                task = infos['task_name']
                distrib = infos['permutation']
                if step in ('setup', 'requirements'):
                    key = '%s_%s_%s' % (task, distrib, step)
                    failed_steps[key] = {'os': distrib, 'step': step}
    return failed_steps
예제 #3
0
 def wrapper(*args, **kwargs):
     """
     Decorated function
     """
     try:
         func(*args, **kwargs)
     except Exception as exc:
         log.exception(exc)
         raise Exception(exc)
     finally:
         log.info('Log report available: %s', LOG_FILE)
예제 #4
0
def put_results_from_reports(version, suite, milestone, reports, distribs,
                             description):
    """

    :param version:
    :param suite:
    :param milestone:
    :param reports:
    :param distribs:
    :param description:
    :return:
    """
    nb_res = 0

    start = time.time()

    plan = get_open_plan(version)
    suite_id = get_suite(suite)

    exclude_section = ['unit', 'robot_framework']
    sections = get_sections(suite_id)
    log.info(sections)

    centos_tests = []
    flaky = []
    known_failed = []
    for section in get_sections(suite_id):
        if section.get('name') not in exclude_section:
            cases_suite = get_cases(suite, section.get('name'))

            cases = [case.get('id') for case in cases_suite]
            centos_tests.extend(cases)

            # Retrieve flaky tests from DB
            flaky_cases = [
                case.get('title') for case in cases_suite
                if case.get('refs', '') is not None
                and "flaky" in case.get('refs')
            ]
            flaky.extend(flaky_cases)

            # Retrieve known failed tests from DB
            failed_cases = [
                case.get('title') for case in cases_suite
                if case.get('refs', '') is not None
                and "known_failed" in case.get('refs')
            ]
            known_failed.extend(failed_cases)
예제 #5
0
def parse_index_file(path):
    """
    Parse index.html to retrieve premerge artifacts

    :param path: index.html path
    :type path: string
    :return premerge_name: premerge artifacts name
    :rtype: string
    """
    with open(path, 'r') as content:
        try:
            premerge_name = re.search('href="./(.*)">',
                                      content.read()).group(1)
        except Exception as exc:
            log.info(exc)
            return

    return premerge_name
예제 #6
0
def check_test_case(report, testrail_names):
    """
    Check tests in report are in testrail test suite

    :param report: path report
    :type report: string
    :param testrail_names: test cases already in testrail test suite
    :type testrail_names: list of string
    :return missing_tests: missing tests
    :rtype: list of string
    """

    log.info('check test cases in %s', report.path)
    test_cases = parse_report(report.path)

    missing_tests = [
        modify_testname(test, None) for test in test_cases
        if modify_testname(test, None) not in testrail_names
    ]

    return missing_tests
예제 #7
0
def print_log_file(func):
    """
    Decorator
    Print path to general log file
    Log exception during func execution

    :param func: function to decorate
    :return wrapper decorated function
    """
    log.info('Log report available: %s', LOG_FILE)

    def wrapper(*args, **kwargs):
        """
        Decorated function
        """
        try:
            func(*args, **kwargs)
        except Exception as exc:
            log.exception(exc)
            raise Exception(exc)
        finally:
            log.info('Log report available: %s', LOG_FILE)

    return wrapper
예제 #8
0
def get_reports(version, distribs, url_artifacts=URL_ARTIFACTS):
    """
    Get all reports from artifacts url

    :param version: example: staging-7.1.0.r17062621.69c5697.post-merge.00034526
    :type version: string
    :param distribs: list of expected OS
    :type distribs: list of strings
    :return:
    """
    start = time.time()

    version = ''.join([url_artifacts, version])

    url = os.path.join(version) + '/'
    tmp_dir = tempfile.mkdtemp()
    log.info(url)

    # Download all junit/report.xml in odr artifacts repo
    cmd = ('wget --tries=50 -l 10 -q -r -P {0} '
           '--progress=dot:mega '
           '--accept=*.xml,report.json {1}').format(tmp_dir, url)

    log.info(cmd)

    out = subprocess.call(cmd.split())
    log.info("wget output: %s", str(out))

    paths = find("*.xml", tmp_dir)

    # Filter on distribs
    paths = [
        path for path in paths if any(d.lower() in path for d in distribs)
    ]

    # Trick to get global report
    if tmp_dir not in paths:
        paths.append(tmp_dir)

    log.info("Reports downloaded from %s:\n%s", url, '\n'.join(paths))

    duration = time.time() - start

    return out, paths, duration
예제 #9
0
def check_test_cases(reports, suite):
    """
    Check tests in report are present in testrail

    :param reports: list of reports
    :param suite: testrail suite
    :return
        missing_tests: tests that are missing,
        testrail_name: existing tests
        duration
    :rtype: tuple (dict, list, integer)
    """
    start = time.time()

    missing_tests = defaultdict(list)

    log.info('Get cases from suite: %s', suite)
    testrail_cases = get_cases(suite)
    testrail_names = [
        modify_testname(test['title'], None) for test in testrail_cases
    ]

    for report in reports:
        section = report.section
        log.info("report: %s", report)
        log.info("section: %s", section)

        if section:
            log.debug('check cases in %s', report)
            missing = check_test_case(report, testrail_names)

            if missing:
                missing_tests[section].extend(missing)
                # Avoid doublon
                missing_tests[section] = list(set(missing_tests[section]))

    duration = time.time() - start

    return missing_tests, testrail_names, duration
예제 #10
0
def get_related_artifacts(version, url_artifacts=URL_ARTIFACTS):
    """
    Get related artifacts if exists (for postmerge build essentially)

    :param version: example: staging-7.1.0.r17062621.69c5697.post-merge.00034526
    :type version: string
    :param distribs: list of expected OS
    :type distribs: list of strings
    :return:
    """
    version = ''.join([url_artifacts, version])

    url = os.path.join(version) + '/.related_artifacts/'
    tmp_dir = tempfile.mkdtemp()
    log.info(url)

    # Download the related artifacts url
    cmd = ('wget --tries=50 -l 3 -q -r -P {0} '
           '--progress=dot:mega '
           '{1}').format(tmp_dir, url)

    log.info(cmd)

    out = subprocess.call(cmd.split())
    log.info("wget output: %s", out)

    # Retrieve related artifacts in index.html directly
    related_artifacts_index = find("index.html", tmp_dir)

    if related_artifacts_index:
        index_file = related_artifacts_index[0]
    else:
        return

    related_artifacts = parse_index_file(index_file)

    return related_artifacts
예제 #11
0
def mass_tag_failed(failed_steps, version, suite, exclude_sections, desc):
    """
    Mass tag failed steps, 'setup' or 'requirements' for ODR

    :param failed_steps: failed steps listing
    :type failed_steps:  dictionary
    :param version: testrail version name
    :param suite: testrail test suite
    :param exclude_sections: sections to be ignored
    :param desc: upload description

    """
    plan = get_plan(version)

    # Retrieve section names from testrail test suite
    suite_id = get_suite(suite)
    sections = get_sections(suite_id)
    sections_name = [
        s.get('name') for s in sections
        if s.get('name') not in exclude_sections
    ]

    log.info('Check environment issues')

    # Hack for suite like undelete.cifs or undelete.fuse
    tricky_sections = ['undelete', 'volprot', 'versioning']

    for task, infos in failed_steps.items():
        # Initialize results list for this task
        results_l = []
        for section in sections_name:
            if section in task:
                for t_section in tricky_sections:
                    if t_section in task:
                        s_task = t_section
                        break
                else:
                    s_task = section
                break
        else:
            log.info('No valid section found in testrail: %s', task)
            continue

        log.info('task: %s -> section found: %s', task, s_task)
        log.info('suite: %s', suite)

        # Get testrail section id
        cases = get_cases(suite, s_task)
        section_id = get_section(suite_id, s_task)

        distrib = infos['os']
        step = infos['step']

        # Handle setup and requirements failures
        if step == 'setup':
            status_id = 8
        elif step == 'requirements':
            status_id = 9
        else:
            raise Exception(
                'Mass tag step not handled, must be "setup" or "requirements"')

        log.info(step)
        log.info(distrib)

        # Get all tests related to the current test run
        run_id = get_run(plan, distrib)
        tests = get_tests(run_id)

        # List all test cases related to current section
        cases_name = [
            case['id'] for case in cases if case['section_id'] == section_id
        ]

        # List all tests related to current section
        tests = [test for test in tests if test['case_id'] in cases_name]

        # Loop on all tests, build dict result and add to results list
        for test in tests:
            result = {
                'test_id': test['id'],
                'status_id': status_id,
                'comment': '{0}\n{1} failed'.format(desc, step),
                'version': version
            }
            results_l.append(result)

        # Put all results in one POST for this current task
        log.info('Put env issues: %s - %s - %s', s_task, distrib, step)
        nb_per_slice = 1000
        nb_slices = len(results_l) / nb_per_slice + 1
        for idx in range(nb_slices):
            put_results(run_id,
                        results_l[nb_per_slice * idx:nb_per_slice * (idx + 1)],
                        tests)
예제 #12
0
def struc_reports(reports, suite, distribs, exclude_sections):
    """
    Build report objects

    report_obj namedtuple has 3 fields:
    - path
    - section
    - distrib

    :param reports: report paths list
    :type reports: list
    :param suite: test suite name in testrail, e.g. '7.2'
    :type suite: string
    :param distribs: os name(s)
    :type distribs: list of string
    :param exclude_sections: sections to ignore
    :type exclude_sections: list of strings
    :return: list of report object
    :rtype: list of `report_obj`
    """
    # Handle directory as report argument
    if isinstance(reports, str):
        reports = [reports]

    dirs = [r for r in reports if os.path.isdir(r)]

    log.info(dirs)

    # Convert each directory to a list of xml report
    global_reports = []
    for c_dir in dirs:
        reports_xml = find("*.xml", c_dir)
        global_reports = find("report.json", c_dir)
        log.info(reports_xml)
        reports.remove(c_dir)
        reports.extend(reports_xml)

    global_reports = found_global_report(global_reports)

    reports_l = []

    # Retrieve section names from testrail test suite
    suite_id = get_suite(suite)
    sections = get_sections(suite_id)
    sections_name = [
        s.get('name') for s in sections
        if s.get('name') not in exclude_sections
    ]

    log.info('Sections: %s', sections_name)

    for report in reports:
        c_section = None
        c_distrib = None
        for section in sections_name:
            if 'undelete' in report:
                # fuse or cifs could be in the path
                c_section = 'undelete'
            elif 'versioning' in report:
                # fuse or cifs could be in the path
                c_section = 'versioning'
            elif 'volprot' in report:
                # fuse or cifs could be in the path
                c_section = 'volprot'
            elif 'robot_framework' in report:
                # sfused could be in the path
                c_section = 'robot_framework'
            elif section in report:
                c_section = section

        for distrib in distribs:
            if distrib.lower() in report:
                c_distrib = distrib.lower()
                break
        else:
            # Handle particular cases here
            if (c_section == 'robot_framework' or c_section == 'unit'
                    or c_section == 'ucheck'):
                # Distrib is not in the path
                c_distrib = 'trusty'

        reports_l.append(report_obj(report, c_section, c_distrib))

    return list(set(reports_l)), global_reports
예제 #13
0
def add_testcases(suite, tests, testrail_cases_name):
    """
    Add test case(s) to a test suite

    :param suite: testrail test suite (ex: "7.2")
    :type suite: string
    :param tests: tests to add
    :type tests: dictionary, each key is a section and contains a list of tests
    :param testrail_cases_name: test cases already in testrail test suite
    :type testrail_cases_name: list of string
    :return: nb_new_tests
    :rtype: integer
    """
    start = time.time()
    nb_new_tests = 0

    for section in tests:
        log.info('Section: %s', section)
        log.info('Suite: %s', suite)
        suite_id = get_suite(suite)
        log.info('Suite id: %s', suite_id)
        section_id = get_section(suite_id, section)
        log.info('Tests to be added: %s', tests)

        for test in tests[section]:
            test = modify_testname(test, section)
            log.info('Adding %s in section %s', test, section)
            ret = add_testcase(test, section_id, testrail_cases_name)
            if ret == 200:
                nb_new_tests += 1
                testrail_cases_name.append(test)
            else:
                log.info('%s test not added', test)

    duration = time.time() - start

    return nb_new_tests, duration
예제 #14
0
def main():
    """
    Entry point
    """
    # Parse arguments
    parser, args = arg_parse()
    add_results = args.add_results
    cases = args.cases
    version = args.version
    reports = args.reports
    artifacts = args.artifacts
    distribs = args.distrib
    exclude_sections = args.exclude_sections
    pattern_plans = args.close_pattern_plans
    upload_location = args.artifacts_location
    milestone = args.milestone
    old_artifacts = args.old_artifacts
    base_url = args.base_url
    linkfile = args.linkfile

    if not milestone:
        milestone = cases

    if not distribs:
        distribs = OS

    if reports:
        reports = [r.decode('utf-8') for r in reports]

    # Handle various parameters combinations
    if not add_results and not pattern_plans:
        parser.print_help()
        raise ArgumentError(None,
                            'Please add results (-u) or close plans (-k)')

    elif add_results and version and cases:
        log.info("Version: %s", version)
        log.info("Suite: %s", cases)

        if artifacts:
            if base_url:
                url_artifacts = os.path.dirname(base_url) + '/'
            elif old_artifacts:
                url_artifacts = URL_ARTIFACTS_OLD
            else:
                url_artifacts = URL_ARTIFACTS

            log.info("Artifacts: %s", artifacts)

            # Get related artifacts
            related_artifacts = get_related_artifacts(artifacts, url_artifacts)

            reports_related = []
            if related_artifacts:
                log.info('Found related artifacts: %s', related_artifacts)

                # Get all reports from related artifacts
                out, reports_related, dur_g = get_reports(
                    related_artifacts,
                    distribs=["robot_framework", "unit", "ucheck"],
                    url_artifacts=URL_ARTIFACTS
                )  # /!\ Warning old artifacts url used here

            # Get all reports from artifacts
            out, reports, dur_g = get_reports(artifacts, distribs,
                                              url_artifacts)

            reports = reports_related + reports

            if not reports:
                raise Exception("No report found")
            log.debug("Get reports output: %s", out)

            upload_location = os.path.join(URL_ARTIFACTS_PUBLIC, artifacts)

        if reports:
            if not upload_location:
                upload_location = reports

            description = ("""
                ***\n
                # Upload infos #\n
                + Last upload: {0}\n
                + hostname: {1}\n
                + user: {2}\n
                + artifacts: [{3}]({3})\n
                ***\n
                """.format(time.asctime(), socket.gethostname(),
                           getpass.getuser(), upload_location))
            description = textwrap.dedent(description)

            # Build reports as object
            reports, global_reports = struc_reports(reports, cases, distribs,
                                                    exclude_sections)

            log.info(version)
            log.info(reports)

            # Get missing tests
            missing, present, dur_c = check_test_cases(reports, cases)
            nb_missing = sum([len(tests) for _, tests in missing.items()])
            log.info('%s Missing tests: %s', nb_missing, missing)

            # Add missing tests cases in test suite if need be
            nb_new_tests, dur_a = add_testcases(cases, missing, present)

            # Put results into testrail DB
            nb_res, dur_p, plan = put_results_from_reports(
                version, cases, milestone, reports, distribs, description)

            # Handle step failures
            # Put env_issue status
            log.info("g_reports %s", global_reports)
            for g_report in global_reports:
                failed_steps = parse_global_report(g_report)
                log.info('failed steps: %s', failed_steps)
                mass_tag_failed(failed_steps, version, cases, exclude_sections,
                                description)

            if artifacts:
                log.info("* Download reports in %s seconds", dur_g)

            log.info("* Check existing test cases in %s seconds", dur_c)

            if nb_new_tests:
                log.info("* Add %s new tests in %s seconds", nb_new_tests,
                         dur_a)

            log.info("* Put %s results in %s seconds", nb_res, dur_p)

            if args.close_plan:
                log.info("Closing plan %s", plan)
                close_plan(plan)

            # Display test plan url
            url_plan = (
                "https://.testrail.net/index.php?/plans/view/{0}".format(plan))
            log.info("Testrail plan: %s", url_plan)
            if linkfile:
                with open(linkfile, 'w') as file_:
                    file_.write(url_plan)

        else:
            raise ArgumentError(None,
                                'Need an artifact url OR a list of reports)')
    elif pattern_plans:
        close_plans(pattern_plans)
    else:
        parser.print_help()
예제 #15
0
                case.get('title') for case in cases_suite
                if case.get('refs', '') is not None
                and "known_failed" in case.get('refs')
            ]
            known_failed.extend(failed_cases)

    if not plan:
        add_plan(version, milestone, description)
        plan = get_plan(version)
        add_plan_entry(plan, suite_id, [1, 2, 3], centos_tests)

    assert plan, "No plan found linked to test suite {0}".format(version)

    entries_id = get_entries_id(plan)
    for entry_id, config in entries_id:
        log.info('Update config: %s run (entry_id): %s', config, entry_id)
        update_plan_entry(plan, entry_id, description)

    # Loop on distribution (one distrib per run)
    for distrib in distribs:
        log.info(distrib)

        run = get_run(plan, distrib)
        assert run, "No run found linked to plan {0}".format(plan)

        tests_db = get_tests(run)
        results = []

        # Loop on report related to distrib
        for report in reports:
            if report.distrib == distrib.lower() and report.section: