Skip to content

Commit

Permalink
test: calculate incremental unittest coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Abingcbc committed Sep 25, 2024
1 parent 5862d33 commit bea03ba
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-core-ut.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ jobs:
run: make unittest_core

- name: Unit Test Coverage
run: docker build -t unittest_coverage -f ./docker/Dockerfile_coverage . && docker run -v $(pwd):$(pwd) unittest_coverage bash -c "cd $(pwd)/core && gcovr --root . --lcov coverage.lcov --txt coverage.txt -e \".*sdk.*\" -e \".*observer.*\" -e \".*protobuf.*\" -e \".*unittest.*\" -e \".*config_server.*\" -e \".*fuse.*\" -e \".*go_pipeline.*\""
run: docker build -t unittest_coverage -f ./docker/Dockerfile_coverage . && docker run -v $(pwd):$(pwd) unittest_coverage bash -c "cd $(pwd)/core && gcovr --root . --json coverage.json --json-summary-pretty --json-summary summary.json -e \".*sdk.*\" -e \".*observer.*\" -e \".*logger.*\" -e \".*unittest.*\" -e \".*config_server.*\" -e \".*go_pipeline.*\" -e \".*application.*\" -e \".*protobuf.*\" -e \".*runner.*\""

- name: Setup Python3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Report code coverage
run: python3 tools/coverage-diff/main.py core/coverage.txt
run: python3 tools/coverage-diff/main.py --path core/coverage.json --summary core/summary.json

result:
runs-on: arc-runner-set-ilogtail
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile_coverage
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ RUN python3 -m pip install --upgrade pip
RUN cp /usr/local/python3/bin/pip3 /usr/bin/pip3 && pip3 install gcovr==7.0
RUN cp /usr/local/python3/bin/gcovr /usr/bin/gcovr

CMD ["bash", "-c", "gcovr --root . --lcov coverage.lcov --txt coverage.txt -e \".*sdk.*\" -e \".*observer.*\" -e \".*lo.*\" -e \".*unittest.*\" -e \".*config_server.*\" -e \".*fuse.*\" -e \".*go_pipeline.*\""]
CMD ["bash", "-c", "gcovr --root . --json coverage.json --json-summary-pretty --json-summary summary.json -e \".*sdk.*\" -e \".*observer.*\" -e \".*logger.*\" -e \".*unittest.*\" -e \".*config_server.*\" -e \".*go_pipeline.*\" -e \".*application.*\" -e \".*protobuf.*\" -e \".*runner.*\""]
129 changes: 90 additions & 39 deletions tools/coverage-diff/main.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,104 @@
import argparse
import subprocess
import sys
import time
import json
import re

ERROR_COLOR = '\033[31m'
RESET_COLOR = '\033[0m'

def get_changed_files():
try:
# Run the git command to get the list of changed files
result = subprocess.Popen('git diff --name-only -r HEAD^1 HEAD', shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Split the result by new line to get each file name
out, err = result.communicate()
changed_files = out.splitlines()
result_files = []
for file in changed_files:
fileStr = file.decode('utf-8')
if fileStr.startswith('core'):
result_files.append(fileStr[5:])
return result_files
result = subprocess.check_output(['git', 'diff', '--unified=0', 'HEAD^1' ,'HEAD'], universal_newlines=True)
return result
except subprocess.CalledProcessError as e:
print(f"An error occurred while running git command: {e}")
print(f'An error occurred while running git command: {e}')
return []

def parse_diff(diff_output):
changes = {}

current_file = None
for line in diff_output.split('\n'):
# 识别文件名
file_match = re.match(r'^diff --git a/(.*) b/(.*)$', line)
if file_match:
current_file = file_match.group(2)
changes[current_file] = []
continue

# 识别文件中的行变化
hunk_match = re.match(r'^@@ -\d+(,\d+)? \+(\d+)(,(\d+))? @@', line)
if hunk_match and current_file:
start_line = int(hunk_match.group(2))
line_count = int(hunk_match.group(4) if hunk_match.group(4) else 1)
for i in range(start_line, start_line + line_count):
changes[current_file].append(i)

return changes

if __name__ == '__main__':
parser = argparse.ArgumentParser(description="A simple argparse example")
parser.add_argument("path", type=str, help="The path of coverage file")
parser = argparse.ArgumentParser(description='A simple argparse example')
parser.add_argument('--path', type=str, help='The path of coverage file')
parser.add_argument('--summary_path', type=str, help='The path of coverage file')
args = parser.parse_args()
changed_files = get_changed_files()
line_cache = ""
not_satified = []
changed_lines = parse_diff(changed_files)

with open(args.summary_path, 'r') as file:
summary = json.load(file)
print('='*20)
print('Total coverage rate: ', summary['line_percent'], '%')
print('='*20)

with open(args.path, 'r') as file:
for line in file:
if len(line_cache) > 0:
line = line_cache + line
line_cache = ""
if '/' in line or ('%' in line and 'TOTAL' not in line):
for changed_file in changed_files:
if line.startswith(changed_file):
units = line.split()
if len(units) < 4:
# some files with long filename will be split into 2 lines
line_cache = line
continue
coverage_rate = int(units[3][:-1])
if coverage_rate < 50:
not_satified.append(changed_file)
print(line, flush=True)
break
else:
print(line, flush=True)
if len(not_satified) > 0:
print(f"Coverage rate is less than 50% for the following files: {not_satified}", flush=True)
coverage = json.load(file)
not_satified = {}
not_satified_count = 0
satified_count = 0

for file in coverage['files']:
if 'core/' + file['file'] in changed_lines:
file_name = 'core/' + file['file']
cur_satified = []
cur_not_satified = []
i = 0
j = 0
while i < len(file['lines']) and j < len(changed_lines[file_name]):
if file['lines'][i]['line_number'] == changed_lines[file_name][j]:
if file['lines'][i]['count'] == 0:
cur_not_satified.append(file['lines'][i]['line_number'])
else:
cur_satified.append(file['lines'][i]['line_number'])
i += 1
j += 1
elif file['lines'][i]['line_number'] < changed_lines[file_name][j]:
i += 1
else:
j += 1
if len(cur_satified) > 0 or len(cur_not_satified) > 0:
print('file: ', file_name)
if len(cur_satified) > 0:
print('covered lines: ', cur_satified)
satified_count += len(cur_satified)
if len(cur_not_satified) > 0:
print(f'{ERROR_COLOR}not covered lines:{RESET_COLOR} ', cur_not_satified)
not_satified_count += len(cur_not_satified)
print('')
if len(cur_not_satified) > 0:
not_satified[file_name] = cur_not_satified

if not_satified_count + satified_count == 0:
print('No line to cover', flush=True)
sys.exit(0)

coverage_rate = ((satified_count) / (not_satified_count + satified_count) ) * 100
print('='*20)
if coverage_rate < 50:
print(f'{ERROR_COLOR}Diff coverage rate is less than 50%: {coverage_rate:.1f}%{RESET_COLOR}', flush=True)
print('='*20)
sys.exit(1)
else:
print(f'Diff coverage rate is {coverage_rate:.1f}%', flush=True)
print('='*20)
sys.exit(0)

0 comments on commit bea03ba

Please sign in to comment.