Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: calculate incremental unittest coverage #1783

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Loading