1
0

coverage_check.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. # Copyright 2021-2025 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. import argparse
  12. import subprocess
  13. import sys
  14. import xmltodict
  15. def check_total_coverage(coverage_file, threshold=80):
  16. """Check the total project coverage."""
  17. with open(coverage_file) as f:
  18. data = xmltodict.parse(f.read())
  19. total_coverage = float(data["coverage"]["@line-rate"]) * 100
  20. print(f"Total Coverage: {total_coverage:.2f}%") # noqa: T201
  21. if total_coverage < threshold:
  22. print(f"Total project coverage is below {threshold}%: {total_coverage:.2f}%") # noqa: T201
  23. sys.exit(1)
  24. def check_changed_files_coverage(coverage_file, changed_files, threshold=80):
  25. """Check the coverage of changed files."""
  26. with open(coverage_file) as f:
  27. data = xmltodict.parse(f.read())
  28. # Handle multiple packages in the coverage report
  29. packages = data["coverage"]["packages"]["package"]
  30. if not isinstance(packages, list):
  31. packages = [packages]
  32. # Extract coverage data for all files
  33. files = {}
  34. for package in packages:
  35. classes = package["classes"]["class"]
  36. if not isinstance(classes, list):
  37. classes = [classes]
  38. for cls in classes:
  39. files[cls["@filename"]] = float(cls["@line-rate"]) * 100
  40. qty = 0
  41. sum_coverage = 0
  42. for file in changed_files:
  43. if file in files:
  44. coverage = files[file]
  45. print(f"Coverage for {file}: {coverage:.2f}%") # noqa: T201
  46. sum_coverage += coverage
  47. qty += 1
  48. else:
  49. print(f"No coverage data found for {file}") # noqa: T201
  50. if qty:
  51. if sum_coverage / qty < threshold:
  52. print(f"Coverage for changed files is below {threshold}%: {sum_coverage/qty:.2f}%") # noqa: T201
  53. sys.exit(1)
  54. print(f"Coverage for changed files: {sum_coverage/qty:.2f}%") # noqa: T201
  55. else:
  56. print("No file detected to run coverage for.") # noqa: T201
  57. def get_changed_files(base_branch):
  58. """Get the list of changed Python files in the pull request."""
  59. try:
  60. result = subprocess.run(
  61. ["git", "diff", "--name-only", f"origin/{base_branch}", "--", "*.py"],
  62. capture_output=True,
  63. text=True,
  64. check=True,
  65. )
  66. changed_files = [
  67. file.replace("taipy/", "")
  68. for file in result.stdout.strip().splitlines()
  69. if not file.startswith(("tests/", "tools/"))
  70. ]
  71. return changed_files
  72. except subprocess.CalledProcessError as e:
  73. print(f"Error fetching changed files: {e}") # noqa: T201
  74. sys.exit(1)
  75. if __name__ == "__main__":
  76. parser = argparse.ArgumentParser(description="Coverage check script.")
  77. parser.add_argument("command", choices=["check-total", "check-changed"], help="Command to execute")
  78. parser.add_argument("--coverage-file", default="coverage.xml", help="Path to the coverage XML file")
  79. parser.add_argument("--threshold", type=float, default=80, help="Coverage threshold percentage")
  80. parser.add_argument("--base-branch", help="Base branch for comparing changed files")
  81. args = parser.parse_args()
  82. if args.command == "check-total":
  83. check_total_coverage(args.coverage_file, args.threshold)
  84. elif args.command == "check-changed":
  85. if not args.base_branch:
  86. print("Error: --base-branch is required for check-changed") # noqa: T201
  87. sys.exit(1)
  88. changed_files = get_changed_files(args.base_branch)
  89. if not changed_files:
  90. print("No relevant Python files changed.") # noqa: T201
  91. sys.exit(0)
  92. check_changed_files_coverage(args.coverage_file, changed_files, args.threshold)