<commit_before>#!/usr/bin/env python

import xml.etree.ElementTree as ElementTree
import sys

# This parses buck-out/gen/jacoco/code-coverage/index.html after
# `buck test --all --code-coverage --code-coverage-format xml --no-results-cache`
# has been run.
PATH_TO_CODE_COVERAGE_XML = 'buck-out/gen/jacoco/code-coverage/coverage.xml'

# If the code coverage for the project drops below this threshold,
# fail the build. This is designed to far enough below our current
# standards (80% coverage) that this should be possible to sustain
# given the inevitable ebb and flow of the code coverage level.
CODE_COVERAGE_BY_LINE_GOAL = 78


def is_covered_package_name(package_name):
    """We exclude third-party code and code generated by javacc."""
    if not package_name.startswith('com/facebook/buck/'):
        return False
    if package_name == 'com/facebook/buck/apple/xcode/xcconfig':
        # TODO: This package has a mix of handwritten and autogenerated code.
        # This script should be updated to include the handwritten code in the
        # metrics.
        return False
    return True


def calculate_code_coverage():
    root = ElementTree.parse(PATH_TO_CODE_COVERAGE_XML)

    line_coverage = []
    max_package_name = 0

    total_line_covered = 0
    total_line = 0

    for element in root.findall('.//package'):
        package_name = element.attrib['name']
        if not is_covered_package_name(package_name):
            continue

        max_package_name = max(max_package_name, len(package_name))
        for counter in element.findall('./counter'):
            counter_type = counter.attrib.get('type')
            missed = int(counter.attrib.get('missed'))
            covered = int(counter.attrib.get('covered'))

            percentage = round(100 * covered / float(missed + covered), 2)
            if counter_type == 'LINE':
                total_line_covered += covered
                total_line += missed + covered
                line_coverage.append({'package_name': package_name, 'percentage': percentage})

    def pair_compare(p1, p2):
        # High percentage should be listed first.
        diff1 = cmp(p2['percentage'], p1['percentage'])
        if diff1:
            return diff1
        # Ties are broken by lexicographic comparison.
        return cmp(p1['package_name'], p2['package_name'])

    def label_with_padding(label):
        return label + ' ' * (max_package_name - len(label)) + ' '

    def print_separator():
        print '-' * (max_package_name + 7)

    # Print header.
    print label_with_padding('PACKAGE') + 'LINE'
    print_separator()

    # Print rows sorted by line coverage then package name.
    line_coverage.sort(cmp=pair_compare)
    for item in line_coverage:
        package_name = item['package_name']
        padding = ' ' * (max_package_name - len(package_name))
        percentage = item['percentage']
        color = '\033[92m' if percentage >= CODE_COVERAGE_BY_LINE_GOAL else '\033[93m'
        print '%s%s%s%%\033[0m' % (color, label_with_padding(package_name), percentage)

    # Print aggregate numbers.
    total_line_percentage = round(100 * total_line_covered / float(total_line), 2)
    color = '\033[92m' if total_line_percentage >= CODE_COVERAGE_BY_LINE_GOAL else '\033[93m'
    print_separator()
    print '%s%s%s%%\033[0m' % (color, label_with_padding('TOTAL'), total_line_percentage)

    return total_line_percentage


def is_code_coverage_met():
    coverage = calculate_code_coverage()
    return coverage >= CODE_COVERAGE_BY_LINE_GOAL


def main():
    """Exits with 0 or 1 depending on whether the code coverage goal is met."""
    if not is_code_coverage_met():
        sys.exit(1)


if __name__ == '__main__':
    main()<commit_msg>Verify against INSTRUCTION coverage. Now assert_code_coverage.py shows all metrics.<commit_after>#!/usr/bin/env python

import xml.etree.ElementTree as ElementTree
import sys

# This parses buck-out/gen/jacoco/code-coverage/index.html after
# `buck test --all --code-coverage --code-coverage-format xml --no-results-cache`
# has been run.
PATH_TO_CODE_COVERAGE_XML = 'buck-out/gen/jacoco/code-coverage/coverage.xml'

# If the code coverage for the project drops below this threshold,
# fail the build. This is designed to far enough below our current
# standards (80% coverage) that this should be possible to sustain
# given the inevitable ebb and flow of the code coverage level.
CODE_COVERAGE_GOAL = 78


def is_covered_package_name(package_name):
    """We exclude third-party code and code generated by javacc."""
    if not package_name.startswith('com/facebook/buck/'):
        return False
    if package_name == 'com/facebook/buck/apple/xcode/xcconfig':
        # TODO: This package has a mix of handwritten and autogenerated code.
        # This script should be updated to include the handwritten code in the
        # metrics.
        return False
    return True


def calculate_code_coverage():
    root = ElementTree.parse(PATH_TO_CODE_COVERAGE_XML)

    # The length of the longest Java package included in the report.
    # Used for display purposes.
    max_package_name = 0

    # Coverage types measured by Jacoco and present in the report.
    # DO NOT ALPHA-SORT. List determines column display order in final report.
    TYPES = [
        'INSTRUCTION',
        'LINE',
        'BRANCH',
        'METHOD',
        'CLASS',
        'COMPLEXITY',
    ]

    # Column by which rows will be sorted in the final report.
    SORT_TYPE = TYPES[0]

    # Keys are values from TYPES; values are integers.
    total_covered_by_type = {}
    total_missed_plus_covered_type = {}
    for coverage_type in TYPES:
        total_covered_by_type[coverage_type] = 0
        total_missed_plus_covered_type[coverage_type] = 0

    # Values are dicts. Will have key 'package_name' as well as all values
    # from TYPES as keys. For entries from TYPES, values are the corresponding
    # coverage percentage for that type.
    coverage_by_package = []

    for element in root.findall('.//package'):
        package_name = element.attrib['name']
        if not is_covered_package_name(package_name):
            continue

        max_package_name = max(max_package_name, len(package_name))
        coverage = {}
        coverage['package_name'] = package_name
        coverage_by_package.append(coverage)
        for counter in element.findall('./counter'):
            counter_type = counter.attrib.get('type')
            missed = int(counter.attrib.get('missed'))
            covered = int(counter.attrib.get('covered'))

            percentage = round(100 * covered / float(missed + covered), 2)
            total_covered_by_type[counter_type] += covered
            total_missed_plus_covered_type[counter_type] += missed + covered
            coverage[counter_type] = percentage

    def pair_compare(p1, p2):
        # High percentage should be listed first.
        diff1 = cmp(p2[SORT_TYPE], p1[SORT_TYPE])
        if diff1:
            return diff1
        # Ties are broken by lexicographic comparison.
        return cmp(p1['package_name'], p2['package_name'])

    def label_with_padding(label):
        return label + ' ' * (max_package_name - len(label)) + ' '

    def print_separator():
        print '-' * (max_package_name + 1 + len(TYPES) * 8)

    def get_color_for_percentage(percentage):
        # \033[92m is OKGREEN.
        # \033[93m is WARNING.
        return '\033[92m' if percentage >= CODE_COVERAGE_GOAL else '\033[93m'

    # Print header.
    # Type column headers are right-justified and truncated to 7 characters.
    column_names = map(lambda x: x[0:7].rjust(7), TYPES)
    print label_with_padding('PACKAGE') + ' ' + ' '.join(column_names)
    print_separator()

    # Create the format string to use for each row.
    format_string = '%(color)s%(label)s'
    for coverage_type in TYPES:
        format_string += '%(' + coverage_type + ')7.2f%%'
    format_string += '\033[0m'

    # Print rows sorted by line coverage then package name.
    coverage_by_package.sort(cmp=pair_compare)
    for item in coverage_by_package:
        info = item.copy()
        info['color'] = get_color_for_percentage(item[SORT_TYPE])
        info['label'] = label_with_padding(item['package_name'])
        print format_string % info

    # Print aggregate numbers.
    overall_percentages = {}
    for coverage_type in TYPES:
        numerator = total_covered_by_type[coverage_type]
        denominator = total_missed_plus_covered_type[coverage_type]
        percentage = 100.0 * numerator / denominator
        overall_percentages[coverage_type] = percentage

    observed_percentage = overall_percentages[SORT_TYPE]
    overall_percentages['color'] = get_color_for_percentage(observed_percentage)
    overall_percentages['label'] = label_with_padding('TOTAL')
    print_separator()
    print format_string % overall_percentages

    return observed_percentage


def main():
    """Exits with 0 or 1 depending on whether the code coverage goal is met."""
    coverage = calculate_code_coverage()
    if coverage < CODE_COVERAGE_GOAL:
        data = {
            'expected': CODE_COVERAGE_GOAL,
            'observed': coverage,
        }
        print '\033[91mFAIL: %(observed).2f%% does not meet goal of %(expected).2f%%\033[0m' % data
        sys.exit(1)


if __name__ == '__main__':
    main()
{'fraction_non_alphanumeric': 0.09768088062413166, 'fraction_numerical': 0.013572726301164904, 'mean_word_length': 3.072236727589208, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7035510', 'n_tokens_mistral': 2921, 'n_tokens_neox': 2804, 'n_words': 760}