Files
pytorch/tools/code_coverage/package/tool/print_report.py

237 lines
7.0 KiB
Python

from __future__ import annotations
import os
import subprocess
from typing import IO, Tuple
from ..oss.utils import get_pytorch_folder
from ..util.setting import SUMMARY_FOLDER_DIR, TestList, TestStatusType
CoverageItem = Tuple[str, float, int, int]
def key_by_percentage(x: CoverageItem) -> float:
return x[1]
def key_by_name(x: CoverageItem) -> str:
return x[0]
def is_intrested_file(file_path: str, interested_folders: list[str]) -> bool:
if "cuda" in file_path:
return False
if "aten/gen_aten" in file_path or "aten/aten_" in file_path:
return False
for folder in interested_folders:
if folder in file_path:
return True
return False
def is_this_type_of_tests(target_name: str, test_set_by_type: set[str]) -> bool:
# tests are divided into three types: success / partial success / fail to collect coverage
for test in test_set_by_type:
if target_name in test:
return True
return False
def print_test_by_type(
tests: TestList, test_set_by_type: set[str], type_name: str, summary_file: IO[str]
) -> None:
print("Tests " + type_name + " to collect coverage:", file=summary_file)
for test in tests:
if is_this_type_of_tests(test.name, test_set_by_type):
print(test.target_pattern, file=summary_file)
print(file=summary_file)
def print_test_condition(
tests: TestList,
tests_type: TestStatusType,
interested_folders: list[str],
coverage_only: list[str],
summary_file: IO[str],
summary_type: str,
) -> None:
print_test_by_type(tests, tests_type["success"], "fully success", summary_file)
print_test_by_type(tests, tests_type["partial"], "partially success", summary_file)
print_test_by_type(tests, tests_type["fail"], "failed", summary_file)
print(
"\n\nCoverage Collected Over Interested Folders:\n",
interested_folders,
file=summary_file,
)
print(
"\n\nCoverage Compilation Flags Only Apply To: \n",
coverage_only,
file=summary_file,
)
print(
"\n\n---------------------------------- "
+ summary_type
+ " ----------------------------------",
file=summary_file,
)
def line_oriented_report(
tests: TestList,
tests_type: TestStatusType,
interested_folders: list[str],
coverage_only: list[str],
covered_lines: dict[str, set[int]],
uncovered_lines: dict[str, set[int]],
) -> None:
with open(os.path.join(SUMMARY_FOLDER_DIR, "line_summary"), "w+") as report_file:
print_test_condition(
tests,
tests_type,
interested_folders,
coverage_only,
report_file,
"LINE SUMMARY",
)
for file_name in covered_lines:
covered = covered_lines[file_name]
uncovered = uncovered_lines[file_name]
print(
f"{file_name}\n covered lines: {sorted(covered)}\n unconvered lines:{sorted(uncovered)}",
file=report_file,
)
def print_file_summary(
covered_summary: int, total_summary: int, summary_file: IO[str]
) -> float:
# print summary first
try:
coverage_percentage = 100.0 * covered_summary / total_summary
except ZeroDivisionError:
coverage_percentage = 0
print(
f"SUMMARY\ncovered: {covered_summary}\nuncovered: {total_summary}\npercentage: {coverage_percentage:.2f}%\n\n",
file=summary_file,
)
if coverage_percentage == 0:
print("Coverage is 0, Please check if json profiles are valid")
return coverage_percentage
def print_file_oriented_report(
tests_type: TestStatusType,
coverage: list[CoverageItem],
covered_summary: int,
total_summary: int,
summary_file: IO[str],
tests: TestList,
interested_folders: list[str],
coverage_only: list[str],
) -> None:
coverage_percentage = print_file_summary(
covered_summary, total_summary, summary_file
)
# print test condition (interested folder / tests that are successsful or failed)
print_test_condition(
tests,
tests_type,
interested_folders,
coverage_only,
summary_file,
"FILE SUMMARY",
)
# print each file's information
for item in coverage:
print(
item[0].ljust(75),
(str(item[1]) + "%").rjust(10),
str(item[2]).rjust(10),
str(item[3]).rjust(10),
file=summary_file,
)
print(f"summary percentage:{coverage_percentage:.2f}%")
def file_oriented_report(
tests: TestList,
tests_type: TestStatusType,
interested_folders: list[str],
coverage_only: list[str],
covered_lines: dict[str, set[int]],
uncovered_lines: dict[str, set[int]],
) -> None:
with open(os.path.join(SUMMARY_FOLDER_DIR, "file_summary"), "w+") as summary_file:
covered_summary = 0
total_summary = 0
coverage = []
for file_name in covered_lines:
# get coverage number for this file
covered_count = len(covered_lines[file_name])
total_count = covered_count + len(uncovered_lines[file_name])
try:
percentage = round(covered_count / total_count * 100, 2)
except ZeroDivisionError:
percentage = 0
# store information in a list to be sorted
coverage.append((file_name, percentage, covered_count, total_count))
# update summary
covered_summary = covered_summary + covered_count
total_summary = total_summary + total_count
# sort
coverage.sort(key=key_by_name)
coverage.sort(key=key_by_percentage)
# print
print_file_oriented_report(
tests_type,
coverage,
covered_summary,
total_summary,
summary_file,
tests,
interested_folders,
coverage_only,
)
def get_html_ignored_pattern() -> list[str]:
return ["/usr/*", "*anaconda3/*", "*third_party/*"]
def html_oriented_report() -> None:
# use lcov to generate the coverage report
build_folder = os.path.join(get_pytorch_folder(), "build")
coverage_info_file = os.path.join(SUMMARY_FOLDER_DIR, "coverage.info")
# generage coverage report -- coverage.info in build folder
subprocess.check_call(
[
"lcov",
"--capture",
"--directory",
build_folder,
"--output-file",
coverage_info_file,
]
)
# remove files that are unrelated
cmd_array = (
["lcov", "--remove", coverage_info_file]
+ get_html_ignored_pattern()
+ ["--output-file", coverage_info_file]
)
subprocess.check_call(
# ["lcov", "--remove", coverage_info_file, "--output-file", coverage_info_file]
cmd_array
)
# generate beautiful html page
subprocess.check_call(
[
"genhtml",
coverage_info_file,
"--output-directory",
os.path.join(SUMMARY_FOLDER_DIR, "html_report"),
]
)